From 66710b17b028dc9b679425b0e8f03c366821b290 Mon Sep 17 00:00:00 2001 From: tsteven4 <13596209+tsteven4@users.noreply.github.com> Date: Tue, 16 Apr 2024 08:09:53 -0600 Subject: [PATCH] update libusb to 1.0.27 (#1259) --- mac/libusb/README | 2 +- mac/libusb/Xcode/config.h | 6 - mac/libusb/core.c | 550 +++++++++++++++-------- mac/libusb/descriptor.c | 292 +++++++++++- mac/libusb/hotplug.c | 9 +- mac/libusb/io.c | 133 +++--- mac/libusb/libusb.h | 321 +++++++++---- mac/libusb/libusbi.h | 48 +- mac/libusb/os/darwin_usb.c | 821 ++++++++++++++++++++++------------ mac/libusb/os/darwin_usb.h | 159 ++----- mac/libusb/os/events_posix.c | 40 ++ mac/libusb/os/events_posix.h | 3 + mac/libusb/os/threads_posix.c | 6 +- mac/libusb/strerror.c | 10 +- mac/libusb/sync.c | 43 +- mac/libusb/version.h | 2 +- mac/libusb/version_nano.h | 2 +- 17 files changed, 1659 insertions(+), 788 deletions(-) diff --git a/mac/libusb/README b/mac/libusb/README index 47aaf8106..8e7ea725a 100644 --- a/mac/libusb/README +++ b/mac/libusb/README @@ -1,4 +1,4 @@ -This is libusb-1.0.26 from https://libusb.info/. +This is libusb-1.0.27 from https://libusb.info/. Since we have such problems with people getting libusb successfully built - between the Universal Build issues and the fact that we have to work hard to go find where it's installed diff --git a/mac/libusb/Xcode/config.h b/mac/libusb/Xcode/config.h index 59f346335..c589a0fba 100644 --- a/mac/libusb/Xcode/config.h +++ b/mac/libusb/Xcode/config.h @@ -8,12 +8,6 @@ /* Define to 1 to enable message logging. */ #define ENABLE_LOGGING 1 -/* On 10.12 and later, use newly available clock_*() functions */ -#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200 -/* Define to 1 if you have the `clock_gettime' function. */ -#define HAVE_CLOCK_GETTIME 1 -#endif - /* On 10.6 and later, use newly available pthread_threadid_np() function */ #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 /* Define to 1 if you have the 'pthread_threadid_np' function. */ diff --git a/mac/libusb/core.c b/mac/libusb/core.c index ec429b7cf..ffe33b775 100644 --- a/mac/libusb/core.c +++ b/mac/libusb/core.c @@ -1,7 +1,7 @@ /* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ /* * Core functions for libusb - * Copyright © 2012-2013 Nathan Hjelm + * Copyright © 2012-2023 Nathan Hjelm * Copyright © 2007-2008 Daniel Drake * Copyright © 2001 Johannes Erdfelt * @@ -34,7 +34,7 @@ static const struct libusb_version libusb_version_internal = { LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO, - LIBUSB_RC, "http://libusb.info" }; + LIBUSB_RC, "https://libusb.info" }; static struct timespec timestamp_origin; #if defined(ENABLE_LOGGING) && !defined(USE_SYSTEM_LOGGING_FACILITY) static libusb_log_cb log_handler; @@ -43,6 +43,9 @@ static libusb_log_cb log_handler; struct libusb_context *usbi_default_context; struct libusb_context *usbi_fallback_context; static int default_context_refcnt; +#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) +static usbi_atomic_t default_debug_level = -1; +#endif static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; static struct usbi_option default_context_options[LIBUSB_OPTION_MAX]; @@ -57,12 +60,12 @@ struct list_head active_contexts_list; * * libusb is an open source library that allows you to communicate with USB * devices from user space. For more info, see the - * libusb homepage. + * libusb homepage. * * This documentation is aimed at application developers wishing to * communicate with USB peripherals from their own software. After reviewing * this documentation, feedback and questions can be sent to the - * libusb-devel mailing list. + * libusb-devel mailing list. * * This documentation assumes knowledge of how to operate USB devices from * a software standpoint (descriptors, configurations, interfaces, endpoints, @@ -111,17 +114,18 @@ struct list_head active_contexts_list; * libusb uses stderr for all logging. By default, logging is set to NONE, * which means that no output will be produced. However, unless the library * has been compiled with logging disabled, then any application calls to - * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level), or the setting of the - * environmental variable LIBUSB_DEBUG outside of the application, can result - * in logging being produced. Your application should therefore not close - * stderr, but instead direct it to the null device if its output is - * undesirable. - * - * The libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) function can be - * used to enable logging of certain messages. Under standard configuration, - * libusb doesn't really log much so you are advised to use this function - * to enable all error/warning/ informational messages. It will help debug - * problems with your software. + * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level), + * libusb_init_context, or the setting of the environmental variable + * LIBUSB_DEBUG outside of the application, can result in logging being + * produced. Your application should therefore not close stderr, but instead + * direct it to the null device if its output is undesirable. + * + * The libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) or + * libusb_init_context functions can be used to enable logging of certain + * messages. With the default configuration, libusb will not log much so if + * you are advised to use one of these functions to enable all + * error/warning/informational messages. It will help debug problems with your + * software. * * The logged messages are unstructured. There is no one-to-one correspondence * between messages being logged and success or failure return codes from @@ -137,19 +141,19 @@ struct list_head active_contexts_list; * The LIBUSB_DEBUG environment variable can be used to enable message logging * at run-time. This environment variable should be set to a log level number, * which is interpreted the same as the - * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) parameter. When this - * environment variable is set, the message logging verbosity level is fixed - * and libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) effectively does - * nothing. + * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level), or + * libusb_init_context(&ctx, &(struct libusb_init_option){.option = LIBUSB_OPTION_LOG_LEVEL, .value = {.ival = level}}, 0). + * When the environment variable is set, the message logging verbosity level is + * fixed and setting the LIBUSB_OPTION_LOG_LEVEL option has no effect. * * libusb can be compiled without any logging functions, useful for embedded - * systems. In this case, libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) - * and the LIBUSB_DEBUG environment variable have no effects. + * systems. In this case, neither the LIBUSB_OPTION_LOG_LEVEL option, nor the + * LIBUSB_DEBUG environment variable will have any effect. * * libusb can also be compiled with verbose debugging messages always. When * the library is compiled in this way, all messages of all verbosities are - * always logged. libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) and - * the LIBUSB_DEBUG environment variable have no effects. + * always logged. Again, in this case, neither the LIBUSB_OPTION_LOG_LEVEL + * option, nor the LIBUSB_DEBUG environment variable will have any effect. * * \section remarks Other remarks * @@ -327,23 +331,23 @@ if (cfg != desired) * developed modules may both use libusb. * * libusb is written to allow for these multiple user scenarios. The two - * "instances" of libusb will not interfere: libusb_set_option() calls - * from one user will not affect the same settings for other users, other - * users can continue using libusb after one of them calls libusb_exit(), etc. + * "instances" of libusb will not interfere: an option set by one user will have + * no effect the same option for other users, other users can continue using + * libusb after one of them calls libusb_exit(), etc. * * This is made possible through libusb's context concept. When you - * call libusb_init(), you are (optionally) given a context. You can then pass + * call libusb_init_context(), you are (optionally) given a context. You can then pass * this context pointer back into future libusb functions. * * In order to keep things simple for more simplistic applications, it is * legal to pass NULL to all functions requiring a context pointer (as long as * you're sure no other code will attempt to use libusb from the same process). * When you pass NULL, the default context will be used. The default context - * is created the first time a process calls libusb_init() when no other + * is created the first time a process calls libusb_init_context() when no other * context is alive. Contexts are destroyed during libusb_exit(). * * The default context is reference-counted and can be shared. That means that - * if libusb_init(NULL) is called twice within the same process, the two + * if libusb_init_context(NULL, x, y) is called twice within the same process, the two * users end up sharing the same context. The deinitialization and freeing of * the default context will only happen when the last user calls libusb_exit(). * In other words, the default context is created and initialized when its @@ -413,6 +417,7 @@ if (cfg != desired) * - libusb_get_device_speed() * - libusb_get_iso_packet_buffer() * - libusb_get_iso_packet_buffer_simple() + * - libusb_get_max_alt_packet_size() * - libusb_get_max_iso_packet_size() * - libusb_get_max_packet_size() * - libusb_get_next_timeout() @@ -436,6 +441,7 @@ if (cfg != desired) * - libusb_hotplug_deregister_callback() * - libusb_hotplug_register_callback() * - libusb_init() + * - libusb_init_context() * - libusb_interrupt_event_handler() * - libusb_interrupt_transfer() * - libusb_kernel_driver_active() @@ -931,13 +937,13 @@ uint8_t API_EXPORTED libusb_get_port_number(libusb_device *dev) /** \ingroup libusb_dev * Get the list of all port numbers from root for the specified device * - * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * Since version 1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102 * \param dev a device * \param port_numbers the array that should contain the port numbers * \param port_numbers_len the maximum length of the array. As per the USB 3.0 * specs, the current maximum limit for the depth is 7. * \returns the number of elements filled - * \returns LIBUSB_ERROR_OVERFLOW if the array is too small + * \returns \ref LIBUSB_ERROR_OVERFLOW if the array is too small */ int API_EXPORTED libusb_get_port_numbers(libusb_device *dev, uint8_t *port_numbers, int port_numbers_len) @@ -1049,8 +1055,8 @@ static const struct libusb_endpoint_descriptor *find_endpoint( * \param dev a device * \param endpoint address of the endpoint in question * \returns the wMaxPacketSize value - * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist - * \returns LIBUSB_ERROR_OTHER on other failure + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns \ref LIBUSB_ERROR_OTHER on other failure */ int API_EXPORTED libusb_get_max_packet_size(libusb_device *dev, unsigned char endpoint) @@ -1079,6 +1085,65 @@ out: return r; } +static const struct libusb_endpoint_descriptor *find_alt_endpoint( + struct libusb_config_descriptor *config, + int iface_idx, int altsetting_idx, unsigned char endpoint) +{ + if (iface_idx >= config->bNumInterfaces) { + return NULL; + } + + const struct libusb_interface *iface = &config->interface[iface_idx]; + + if (altsetting_idx >= iface->num_altsetting) { + return NULL; + } + + const struct libusb_interface_descriptor *altsetting + = &iface->altsetting[altsetting_idx]; + int ep_idx; + + for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; ep_idx++) { + const struct libusb_endpoint_descriptor *ep = + &altsetting->endpoint[ep_idx]; + if (ep->bEndpointAddress == endpoint) + return ep; + } + return NULL; +} + +static int get_endpoint_max_packet_size(libusb_device *dev, + const struct libusb_endpoint_descriptor *ep) +{ + struct libusb_ss_endpoint_companion_descriptor *ss_ep_cmp; + enum libusb_endpoint_transfer_type ep_type; + uint16_t val; + int r = 0; + int speed; + + speed = libusb_get_device_speed(dev); + if (speed >= LIBUSB_SPEED_SUPER) { + r = libusb_get_ss_endpoint_companion_descriptor(dev->ctx, ep, &ss_ep_cmp); + if (r == LIBUSB_SUCCESS) { + r = ss_ep_cmp->wBytesPerInterval; + libusb_free_ss_endpoint_companion_descriptor(ss_ep_cmp); + } + } + + /* If the device isn't a SuperSpeed device or retrieving the SS endpoint didn't worked. */ + if (speed < LIBUSB_SPEED_SUPER || r < 0) { + val = ep->wMaxPacketSize; + ep_type = (enum libusb_endpoint_transfer_type) (ep->bmAttributes & 0x3); + + r = val & 0x07ff; + if (ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS + || ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT) + r *= (1 + ((val >> 11) & 3)); + } + + return r; +} + /** \ingroup libusb_dev * Calculate the maximum packet size which a specific endpoint is capable is * sending or receiving in the duration of 1 microframe @@ -1099,24 +1164,25 @@ out: * libusb_set_iso_packet_lengths() in order to set the length field of every * isochronous packet in a transfer. * + * This function only considers the first alternate setting of the interface. + * If the endpoint has different maximum packet sizes for different alternate + * settings, you probably want libusb_get_max_alt_packet_size() instead. + * * Since v1.0.3. * * \param dev a device * \param endpoint address of the endpoint in question * \returns the maximum packet size which can be sent/received on this endpoint - * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist - * \returns LIBUSB_ERROR_OTHER on other failure + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns \ref LIBUSB_ERROR_OTHER on other failure + * \see libusb_get_max_alt_packet_size */ int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev, unsigned char endpoint) { struct libusb_config_descriptor *config; const struct libusb_endpoint_descriptor *ep; - struct libusb_ss_endpoint_companion_descriptor *ss_ep_cmp; - enum libusb_endpoint_transfer_type ep_type; - uint16_t val; int r; - int speed; r = libusb_get_active_config_descriptor(dev, &config); if (r < 0) { @@ -1131,26 +1197,68 @@ int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev, goto out; } - speed = libusb_get_device_speed(dev); - if (speed >= LIBUSB_SPEED_SUPER) { - r = libusb_get_ss_endpoint_companion_descriptor(dev->ctx, ep, &ss_ep_cmp); - if (r == LIBUSB_SUCCESS) { - r = ss_ep_cmp->wBytesPerInterval; - libusb_free_ss_endpoint_companion_descriptor(ss_ep_cmp); - } - } + r = get_endpoint_max_packet_size(dev, ep); - /* If the device isn't a SuperSpeed device or retrieving the SS endpoint didn't worked. */ - if (speed < LIBUSB_SPEED_SUPER || r < 0) { - val = ep->wMaxPacketSize; - ep_type = (enum libusb_endpoint_transfer_type) (ep->bmAttributes & 0x3); +out: + libusb_free_config_descriptor(config); + return r; +} - r = val & 0x07ff; - if (ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS - || ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT) - r *= (1 + ((val >> 11) & 3)); +/** \ingroup libusb_dev + * Calculate the maximum packet size which a specific endpoint is capable of + * sending or receiving in the duration of 1 microframe + * + * Only the active configuration is examined. The calculation is based on the + * wMaxPacketSize field in the endpoint descriptor as described in section + * 9.6.6 in the USB 2.0 specifications. + * + * If acting on an isochronous or interrupt endpoint, this function will + * multiply the value found in bits 0:10 by the number of transactions per + * microframe (determined by bits 11:12). Otherwise, this function just + * returns the numeric value found in bits 0:10. For USB 3.0 device, it + * will attempts to retrieve the Endpoint Companion Descriptor to return + * wBytesPerInterval. + * + * This function is useful for setting up isochronous transfers, for example + * you might pass the return value from this function to + * libusb_set_iso_packet_lengths() in order to set the length field of every + * isochronous packet in a transfer. + * + * Since version 1.0.27, \ref LIBUSB_API_VERSION >= 0x0100010A + * + * \param dev a device + * \param interface_number the bInterfaceNumber of the interface + * the endpoint belongs to + * \param alternate_setting the bAlternateSetting of the interface + * \param endpoint address of the endpoint in question + * \returns the maximum packet size which can be sent/received on this endpoint + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns \ref LIBUSB_ERROR_OTHER on other failure + * \see libusb_get_max_iso_packet_size + */ +int API_EXPORTED libusb_get_max_alt_packet_size(libusb_device *dev, + int interface_number, int alternate_setting, unsigned char endpoint) +{ + struct libusb_config_descriptor *config; + const struct libusb_endpoint_descriptor *ep; + int r; + + r = libusb_get_active_config_descriptor(dev, &config); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), + "could not retrieve active config descriptor"); + return LIBUSB_ERROR_OTHER; + } + + ep = find_alt_endpoint(config, interface_number, + alternate_setting, endpoint); + if (!ep) { + r = LIBUSB_ERROR_NOT_FOUND; + goto out; } + r = get_endpoint_max_packet_size(dev, ep); + out: libusb_free_config_descriptor(config); return r; @@ -1209,10 +1317,10 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev) * handle for the underlying device. The handle allows you to use libusb to * perform I/O on the device in question. * - * Call libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY) before - * libusb_init() if you want to skip enumeration of USB devices. In particular, - * this might be needed on Android if you don't have authority to access USB - * devices in general. + * Call libusb_init_context with the LIBUSB_OPTION_NO_DEVICE_DISCOVERY + * option if you want to skip enumeration of USB devices. In particular, this + * might be needed on Android if you don't have authority to access USB + * devices in general. Setting this option with libusb_set_option is deprecated. * * On Linux, the system device handle must be a valid file descriptor opened * on the device node. @@ -1233,9 +1341,9 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev) * \param dev_handle output location for the returned device handle pointer. Only * populated when the return code is 0. * \returns 0 on success - * \returns LIBUSB_ERROR_NO_MEM on memory allocation failure - * \returns LIBUSB_ERROR_ACCESS if the user has insufficient permissions - * \returns LIBUSB_ERROR_NOT_SUPPORTED if the operation is not supported on this + * \returns \ref LIBUSB_ERROR_NO_MEM on memory allocation failure + * \returns \ref LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if the operation is not supported on this * platform * \returns another LIBUSB_ERROR code on other failure */ @@ -1289,9 +1397,9 @@ int API_EXPORTED libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, * \param dev_handle output location for the returned device handle pointer. Only * populated when the return code is 0. * \returns 0 on success - * \returns LIBUSB_ERROR_NO_MEM on memory allocation failure - * \returns LIBUSB_ERROR_ACCESS if the user has insufficient permissions - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NO_MEM on memory allocation failure + * \returns \ref LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns another LIBUSB_ERROR code on other failure */ int API_EXPORTED libusb_open(libusb_device *dev, @@ -1397,20 +1505,22 @@ static void do_close(struct libusb_context *ctx, for_each_transfer_safe(ctx, itransfer, tmp) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + uint32_t state_flags; if (transfer->dev_handle != dev_handle) continue; usbi_mutex_lock(&itransfer->lock); - if (!(itransfer->state_flags & USBI_TRANSFER_DEVICE_DISAPPEARED)) { + state_flags = itransfer->state_flags; + usbi_mutex_unlock(&itransfer->lock); + if (!(state_flags & USBI_TRANSFER_DEVICE_DISAPPEARED)) { usbi_err(ctx, "Device handle closed while transfer was still being processed, but the device is still connected as far as we know"); - if (itransfer->state_flags & USBI_TRANSFER_CANCELLING) + if (state_flags & USBI_TRANSFER_CANCELLING) usbi_warn(ctx, "A cancellation for an in-flight transfer hasn't completed but closing the device handle"); else usbi_err(ctx, "A cancellation hasn't even been scheduled on the transfer for which the device is closing"); } - usbi_mutex_unlock(&itransfer->lock); /* remove from the list of in-flight transfers and make sure * we don't accidentally use the device handle in the future @@ -1424,7 +1534,7 @@ static void do_close(struct libusb_context *ctx, * the device handle is invalid */ usbi_dbg(ctx, "Removed transfer %p from the in-flight list because device handle %p closed", - transfer, dev_handle); + (void *) transfer, (void *) dev_handle); } usbi_mutex_unlock(&ctx->flying_transfers_lock); @@ -1533,7 +1643,7 @@ libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle) * \param config output location for the bConfigurationValue of the active * configuration (only valid for return code 0) * \returns 0 on success - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns another LIBUSB_ERROR code on other failure */ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle, @@ -1584,7 +1694,7 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle, * endpoint halts cleared, toggles reset). * * Not all backends support setting the configuration from user space, which - * will be indicated by the return code LIBUSB_ERROR_NOT_SUPPORTED. As this + * will be indicated by the return code \ref LIBUSB_ERROR_NOT_SUPPORTED. As this * suggests that the platform is handling the device configuration itself, * this error should generally be safe to ignore. * @@ -1615,11 +1725,11 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle, * wish to activate, or -1 if you wish to put the device in an unconfigured * state * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist - * \returns LIBUSB_ERROR_BUSY if interfaces are currently claimed - * \returns LIBUSB_ERROR_NOT_SUPPORTED if setting or changing the configuration + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist + * \returns \ref LIBUSB_ERROR_BUSY if interfaces are currently claimed + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if setting or changing the configuration * is not supported by the backend - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns another LIBUSB_ERROR code on other failure * \see libusb_set_auto_detach_kernel_driver() */ @@ -1653,10 +1763,10 @@ int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev_handle, * \param interface_number the bInterfaceNumber of the interface you * wish to claim * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the requested interface does not exist - * \returns LIBUSB_ERROR_BUSY if another program or driver has claimed the + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the requested interface does not exist + * \returns \ref LIBUSB_ERROR_BUSY if another program or driver has claimed the * interface - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns a LIBUSB_ERROR code on other failure * \see libusb_set_auto_detach_kernel_driver() */ @@ -1699,8 +1809,8 @@ out: * \param interface_number the bInterfaceNumber of the * previously-claimed interface * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the interface was not claimed + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns another LIBUSB_ERROR code on other failure * \see libusb_set_auto_detach_kernel_driver() */ @@ -1744,9 +1854,9 @@ out: * \param alternate_setting the bAlternateSetting of the alternate * setting to activate * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed, or the + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the interface was not claimed, or the * requested alternate setting does not exist - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns another LIBUSB_ERROR code on other failure */ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_handle, @@ -1760,7 +1870,6 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_hand return LIBUSB_ERROR_INVALID_PARAM; if (!usbi_atomic_load(&dev_handle->dev->attached)) { - usbi_mutex_unlock(&dev_handle->lock); return LIBUSB_ERROR_NO_DEVICE; } @@ -1787,8 +1896,8 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_hand * \param dev_handle a device handle * \param endpoint the endpoint to clear halt status * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns another LIBUSB_ERROR code on other failure */ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle, @@ -1809,14 +1918,14 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle, * If the reset fails, the descriptors change, or the previous state cannot be * restored, the device will appear to be disconnected and reconnected. This * means that the device handle is no longer valid (you should close it) and - * rediscover the device. A return code of LIBUSB_ERROR_NOT_FOUND indicates + * rediscover the device. A return code of \ref LIBUSB_ERROR_NOT_FOUND indicates * when this is the case. * * This is a blocking function which usually incurs a noticeable delay. * * \param dev_handle a handle of the device to reset * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the + * \returns \ref LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the * device has been disconnected * \returns another LIBUSB_ERROR code on other failure */ @@ -1881,7 +1990,7 @@ int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle, * \param dev_handle a device handle * \param endpoints array of endpoints to free streams on * \param num_endpoints length of the endpoints array - * \returns LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure + * \returns \ref LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure */ int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle, unsigned char *endpoints, int num_endpoints) @@ -1944,7 +2053,7 @@ unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handl * \param dev_handle a device handle * \param buffer pointer to the previously allocated memory * \param length size of previously allocated memory - * \returns LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure + * \returns \ref LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure */ int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle, unsigned char *buffer, size_t length) @@ -1966,8 +2075,8 @@ int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle, * \param interface_number the interface to check * \returns 0 if no kernel driver is active * \returns 1 if a kernel driver is active - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality * is not available * \returns another LIBUSB_ERROR code on other failure * \see libusb_detach_kernel_driver() @@ -1997,15 +2106,15 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle, * * Note that libusb itself also talks to the device through a special kernel * driver, if this driver is already attached to the device, this call will - * not detach it and return LIBUSB_ERROR_NOT_FOUND. + * not detach it and return \ref LIBUSB_ERROR_NOT_FOUND. * * \param dev_handle a device handle * \param interface_number the interface to detach the driver from * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active - * \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * \returns \ref LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality * is not available * \returns another LIBUSB_ERROR code on other failure * \see libusb_kernel_driver_active() @@ -2036,12 +2145,12 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle, * \param dev_handle a device handle * \param interface_number the interface to attach the driver from * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active - * \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * \returns \ref LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality * is not available - * \returns LIBUSB_ERROR_BUSY if the driver cannot be attached because the + * \returns \ref LIBUSB_ERROR_BUSY if the driver cannot be attached because the * interface is claimed by a program or driver * \returns another LIBUSB_ERROR code on other failure * \see libusb_kernel_driver_active() @@ -2072,14 +2181,14 @@ int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle, * handles by default. * * On platforms which do not have LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER - * this function will return LIBUSB_ERROR_NOT_SUPPORTED, and libusb will + * this function will return \ref LIBUSB_ERROR_NOT_SUPPORTED, and libusb will * continue as if this function was never called. * * \param dev_handle a device handle * \param enable whether to enable or disable auto kernel driver detachment * - * \returns LIBUSB_SUCCESS on success - * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * \returns \ref LIBUSB_SUCCESS on success + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality * is not available * \see libusb_claim_interface() * \see libusb_release_interface() @@ -2096,20 +2205,34 @@ int API_EXPORTED libusb_set_auto_detach_kernel_driver( } /** \ingroup libusb_lib - * \deprecated Use libusb_set_option() instead using the - * \ref LIBUSB_OPTION_LOG_LEVEL option. + * Deprecated. Use libusb_set_option() or libusb_init_context() instead, + * with the \ref LIBUSB_OPTION_LOG_LEVEL option. */ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level) { -#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) - ctx = usbi_get_context(ctx); - if (!ctx->debug_fixed) { - level = CLAMP(level, LIBUSB_LOG_LEVEL_NONE, LIBUSB_LOG_LEVEL_DEBUG); - ctx->debug = (enum libusb_log_level)level; + libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level); +} + +static void libusb_set_log_cb_internal(libusb_context *ctx, libusb_log_cb cb, + int mode) +{ +#if defined(ENABLE_LOGGING) && (!defined(ENABLE_DEBUG_LOGGING) || !defined(USE_SYSTEM_LOGGING_FACILITY)) +#if !defined(USE_SYSTEM_LOGGING_FACILITY) + if (mode & LIBUSB_LOG_CB_GLOBAL) + log_handler = cb; +#endif +#if !defined(ENABLE_DEBUG_LOGGING) + if (mode & LIBUSB_LOG_CB_CONTEXT) { + ctx = usbi_get_context(ctx); + ctx->log_handler = cb; } #else UNUSED(ctx); - UNUSED(level); +#endif +#else + UNUSED(ctx); + UNUSED(cb); + UNUSED(mode); #endif } @@ -2139,24 +2262,7 @@ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level) void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, int mode) { -#if defined(ENABLE_LOGGING) && (!defined(ENABLE_DEBUG_LOGGING) || !defined(USE_SYSTEM_LOGGING_FACILITY)) -#if !defined(USE_SYSTEM_LOGGING_FACILITY) - if (mode & LIBUSB_LOG_CB_GLOBAL) - log_handler = cb; -#endif -#if !defined(ENABLE_DEBUG_LOGGING) - if (mode & LIBUSB_LOG_CB_CONTEXT) { - ctx = usbi_get_context(ctx); - ctx->log_handler = cb; - } -#else - UNUSED(ctx); -#endif -#else - UNUSED(ctx); - UNUSED(cb); - UNUSED(mode); -#endif + libusb_set_log_cb_internal(ctx, cb, mode); } /** \ingroup libusb_lib @@ -2176,72 +2282,95 @@ void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, * \param option which option to set * \param ... any required arguments for the specified option * - * \returns LIBUSB_SUCCESS on success - * \returns LIBUSB_ERROR_INVALID_PARAM if the option or arguments are invalid - * \returns LIBUSB_ERROR_NOT_SUPPORTED if the option is valid but not supported + * \returns \ref LIBUSB_SUCCESS on success + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the option or arguments are invalid + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if the option is valid but not supported * on this platform - * \returns LIBUSB_ERROR_NOT_FOUND if LIBUSB_OPTION_USE_USBDK is valid on this platform but UsbDk is not available + * \returns \ref LIBUSB_ERROR_NOT_FOUND if LIBUSB_OPTION_USE_USBDK is valid on this platform but UsbDk is not available */ -int API_EXPORTED libusb_set_option(libusb_context *ctx, +int API_EXPORTEDV libusb_set_option(libusb_context *ctx, enum libusb_option option, ...) { int arg = 0, r = LIBUSB_SUCCESS; + libusb_log_cb log_cb = NULL; va_list ap; +#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) + int is_default_context = (NULL == ctx); +#endif va_start(ap, option); + if (LIBUSB_OPTION_LOG_LEVEL == option) { arg = va_arg(ap, int); if (arg < LIBUSB_LOG_LEVEL_NONE || arg > LIBUSB_LOG_LEVEL_DEBUG) { r = LIBUSB_ERROR_INVALID_PARAM; } } - va_end(ap); - - if (LIBUSB_SUCCESS != r) { - return r; + if (LIBUSB_OPTION_LOG_CB == option) { + log_cb = (libusb_log_cb) va_arg(ap, libusb_log_cb); } - if (option >= LIBUSB_OPTION_MAX) { - return LIBUSB_ERROR_INVALID_PARAM; - } + do { + if (LIBUSB_SUCCESS != r) { + break; + } - if (NULL == ctx) { - usbi_mutex_static_lock(&default_context_lock); - default_context_options[option].is_set = 1; - if (LIBUSB_OPTION_LOG_LEVEL == option) { - default_context_options[option].arg.ival = arg; + if (option >= LIBUSB_OPTION_MAX) { + r = LIBUSB_ERROR_INVALID_PARAM; + break; } - usbi_mutex_static_unlock(&default_context_lock); - } - ctx = usbi_get_context(ctx); - if (NULL == ctx) { - return LIBUSB_SUCCESS; - } + if (NULL == ctx) { + usbi_mutex_static_lock(&default_context_lock); + default_context_options[option].is_set = 1; + if (LIBUSB_OPTION_LOG_LEVEL == option) { + default_context_options[option].arg.ival = arg; + } else if (LIBUSB_OPTION_LOG_CB == option) { + default_context_options[option].arg.log_cbval = log_cb; + libusb_set_log_cb_internal(NULL, log_cb, LIBUSB_LOG_CB_GLOBAL); + } + usbi_mutex_static_unlock(&default_context_lock); + } - switch (option) { - case LIBUSB_OPTION_LOG_LEVEL: + ctx = usbi_get_context(ctx); + if (NULL == ctx) + break; + + switch (option) { + case LIBUSB_OPTION_LOG_LEVEL: #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) - if (!ctx->debug_fixed) - ctx->debug = (enum libusb_log_level)arg; + if (!ctx->debug_fixed) { + ctx->debug = (enum libusb_log_level)arg; + if (is_default_context) + usbi_atomic_store(&default_debug_level, CLAMP(arg, LIBUSB_LOG_LEVEL_NONE, LIBUSB_LOG_LEVEL_DEBUG)); + } #endif - break; + break; - /* Handle all backend-specific options here */ - case LIBUSB_OPTION_USE_USBDK: - case LIBUSB_OPTION_NO_DEVICE_DISCOVERY: - if (usbi_backend.set_option) - return usbi_backend.set_option(ctx, option, ap); + /* Handle all backend-specific options here */ + case LIBUSB_OPTION_USE_USBDK: + case LIBUSB_OPTION_NO_DEVICE_DISCOVERY: + if (usbi_backend.set_option) { + r = usbi_backend.set_option(ctx, option, ap); + break; + } - return LIBUSB_ERROR_NOT_SUPPORTED; - break; + r = LIBUSB_ERROR_NOT_SUPPORTED; + break; - case LIBUSB_OPTION_MAX: - default: - return LIBUSB_ERROR_INVALID_PARAM; - } + case LIBUSB_OPTION_LOG_CB: + libusb_set_log_cb_internal(ctx, log_cb, LIBUSB_LOG_CB_CONTEXT); + break; - return LIBUSB_SUCCESS;; + case LIBUSB_OPTION_MAX: /* unreachable */ + default: + r = LIBUSB_ERROR_INVALID_PARAM; + } + } while (0); + + va_end(ap); + + return r; } #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) @@ -2264,20 +2393,35 @@ static enum libusb_log_level get_env_debug_level(void) } #endif +/** \ingroup libusb_lib + * Deprecated initialization function. Equivalent to calling libusb_init_context with no options. + * + * \see libusb_init_context + */ +int API_EXPORTED libusb_init(libusb_context **ctx) +{ + return libusb_init_context(ctx, NULL, 0); +} + /** \ingroup libusb_lib * Initialize libusb. This function must be called before calling any other * libusb function. * * If you do not provide an output location for a context pointer, a default * context will be created. If there was already a default context, it will - * be reused (and nothing will be initialized/reinitialized). + * be reused (and nothing will be initialized/reinitialized and options will + * be ignored). If num_options is 0 then options is ignored and may be NULL. + * + * Since version 1.0.27, \ref LIBUSB_API_VERSION >= 0x0100010A * * \param ctx Optional output location for context pointer. * Only valid on return code 0. + * \param options Optional array of options to set on the new context. + * \param num_options Number of elements in the options array. * \returns 0 on success, or a LIBUSB_ERROR code on failure * \see libusb_contexts */ -int API_EXPORTED libusb_init(libusb_context **ctx) +int API_EXPORTED libusb_init_context(libusb_context **ctx, const struct libusb_init_option options[], int num_options) { size_t priv_size = usbi_backend.context_priv_size; struct libusb_context *_ctx; @@ -2293,10 +2437,12 @@ int API_EXPORTED libusb_init(libusb_context **ctx) } /* check for first init */ + usbi_mutex_static_lock(&active_contexts_lock); if (!active_contexts_list.next) { list_init(&active_contexts_list); usbi_get_monotonic_time(×tamp_origin); } + usbi_mutex_static_unlock(&active_contexts_lock); _ctx = calloc(1, PTR_ALIGN(sizeof(*_ctx)) + priv_size); if (!_ctx) { @@ -2305,13 +2451,13 @@ int API_EXPORTED libusb_init(libusb_context **ctx) } #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) - if (NULL == ctx && default_context_options[LIBUSB_OPTION_LOG_LEVEL].is_set) { - _ctx->debug = default_context_options[LIBUSB_OPTION_LOG_LEVEL].arg.ival; - } else { + _ctx->debug = LIBUSB_LOG_LEVEL_NONE; + if (getenv("LIBUSB_DEBUG")) { _ctx->debug = get_env_debug_level(); - } - if (_ctx->debug != LIBUSB_LOG_LEVEL_NONE) _ctx->debug_fixed = 1; + } else if (default_context_options[LIBUSB_OPTION_LOG_LEVEL].is_set) { + _ctx->debug = default_context_options[LIBUSB_OPTION_LOG_LEVEL].arg.ival; + } #endif usbi_mutex_init(&_ctx->usb_devs_lock); @@ -2324,7 +2470,29 @@ int API_EXPORTED libusb_init(libusb_context **ctx) if (LIBUSB_OPTION_LOG_LEVEL == option || !default_context_options[option].is_set) { continue; } - r = libusb_set_option(_ctx, option); + if (LIBUSB_OPTION_LOG_CB != option) { + r = libusb_set_option(_ctx, option); + } else { + r = libusb_set_option(_ctx, option, default_context_options[option].arg.log_cbval); + } + if (LIBUSB_SUCCESS != r) + goto err_free_ctx; + } + + /* apply any options provided by the user */ + for (int i = 0 ; i < num_options ; ++i) { + switch(options[i].option) { + case LIBUSB_OPTION_LOG_CB: + r = libusb_set_option(_ctx, options[i].option, options[i].value.log_cbval); + break; + + case LIBUSB_OPTION_LOG_LEVEL: + case LIBUSB_OPTION_USE_USBDK: + case LIBUSB_OPTION_NO_DEVICE_DISCOVERY: + case LIBUSB_OPTION_MAX: + default: + r = libusb_set_option(_ctx, options[i].option, options[i].value.ival); + } if (LIBUSB_SUCCESS != r) goto err_free_ctx; } @@ -2333,6 +2501,9 @@ int API_EXPORTED libusb_init(libusb_context **ctx) if (!ctx) { usbi_default_context = _ctx; default_context_refcnt = 1; +#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) + usbi_atomic_store(&default_debug_level, _ctx->debug); +#endif usbi_dbg(usbi_default_context, "created default context"); } @@ -2360,8 +2531,12 @@ int API_EXPORTED libusb_init(libusb_context **ctx) *ctx = _ctx; if (!usbi_fallback_context) { +#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) + if (usbi_atomic_load(&default_debug_level) == -1) + usbi_atomic_store(&default_debug_level, _ctx->debug); +#endif usbi_fallback_context = _ctx; - usbi_warn(usbi_fallback_context, "installing new context as implicit default"); + usbi_dbg(usbi_fallback_context, "installing new context as implicit default"); } } @@ -2432,6 +2607,9 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) list_del(&_ctx->list); usbi_mutex_static_unlock(&active_contexts_lock); + /* Exit hotplug before backend dependency */ + usbi_hotplug_exit(_ctx); + if (usbi_backend.exit) usbi_backend.exit(_ctx); @@ -2445,7 +2623,6 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) /* Don't bother with locking after this point because unless there is * an application bug, nobody will be accessing the context. */ - usbi_hotplug_exit(_ctx); usbi_io_exit(_ctx); for_each_device(_ctx, dev) { @@ -2465,7 +2642,7 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) /** \ingroup libusb_misc * Check at runtime if the loaded library has a given capability. - * This call should be performed after \ref libusb_init(), to ensure the + * This call should be performed after \ref libusb_init_context(), to ensure the * backend has updated its capability set. * * \param capability the \ref libusb_capability to check for @@ -2584,13 +2761,14 @@ static void log_v(struct libusb_context *ctx, enum libusb_log_level level, UNUSED(ctx); #else enum libusb_log_level ctx_level; + long default_level_value; - ctx = ctx ? ctx : usbi_default_context; - ctx = ctx ? ctx : usbi_fallback_context; - if (ctx) + if (ctx) { ctx_level = ctx->debug; - else - ctx_level = get_env_debug_level(); + } else { + default_level_value = usbi_atomic_load(&default_debug_level); + ctx_level = default_level_value < 0 ? get_env_debug_level() : (enum libusb_log_level)default_level_value; + } if (ctx_level < level) return; diff --git a/mac/libusb/descriptor.c b/mac/libusb/descriptor.c index 253ef1c31..4623ad1e3 100644 --- a/mac/libusb/descriptor.c +++ b/mac/libusb/descriptor.c @@ -521,7 +521,7 @@ static int get_config_descriptor(struct libusb_device *dev, uint8_t config_idx, * * This is a non-blocking function; the device descriptor is cached in memory. * - * Note since libusb-1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, this + * Note since libusb-1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102, this * function always succeeds. * * \param dev the device @@ -548,7 +548,7 @@ int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev, * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() * after use. * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state * \returns another LIBUSB_ERROR code on error * \see libusb_get_config_descriptor */ @@ -588,7 +588,7 @@ int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev, * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() * after use. * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the configuration does not exist * \returns another LIBUSB_ERROR code on error * \see libusb_get_active_config_descriptor() * \see libusb_get_config_descriptor_by_value() @@ -634,7 +634,7 @@ int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev, * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() * after use. * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the configuration does not exist * \returns another LIBUSB_ERROR code on error * \see libusb_get_active_config_descriptor() * \see libusb_get_config_descriptor() @@ -699,7 +699,7 @@ void API_EXPORTED libusb_free_config_descriptor( * descriptor. Only valid if 0 was returned. Must be freed with * libusb_free_ss_endpoint_companion_descriptor() after use. * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the configuration does not exist * \returns another LIBUSB_ERROR code on error */ int API_EXPORTED libusb_get_ss_endpoint_companion_descriptor( @@ -840,7 +840,7 @@ static int parse_bos(struct libusb_context *ctx, * \param bos output location for the BOS descriptor. Only valid if 0 was returned. * Must be freed with \ref libusb_free_bos_descriptor() after use. * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the device doesn't have a BOS descriptor + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the device doesn't have a BOS descriptor * \returns another LIBUSB_ERROR code on error */ int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle, @@ -1070,6 +1070,70 @@ void API_EXPORTED libusb_free_container_id_descriptor( free(container_id); } +/** \ingroup libusb_desc + * Get a platform descriptor + * + * Since version 1.0.27, \ref LIBUSB_API_VERSION >= 0x0100010A + * + * \param ctx the context to operate on, or NULL for the default context + * \param dev_cap Device Capability descriptor with a bDevCapabilityType of + * \ref libusb_capability_type::LIBUSB_BT_PLATFORM_DESCRIPTOR + * LIBUSB_BT_PLATFORM_DESCRIPTOR + * \param platform_descriptor output location for the Platform descriptor. + * Only valid if 0 was returned. Must be freed with + * libusb_free_platform_descriptor() after use. + * \returns 0 on success + * \returns a LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_platform_descriptor(libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_platform_descriptor **platform_descriptor) +{ + struct libusb_platform_descriptor *_platform_descriptor; + + if (dev_cap->bDevCapabilityType != LIBUSB_BT_PLATFORM_DESCRIPTOR) { + usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)", + dev_cap->bDevCapabilityType, + LIBUSB_BT_PLATFORM_DESCRIPTOR); + return LIBUSB_ERROR_INVALID_PARAM; + } else if (dev_cap->bLength < LIBUSB_BT_PLATFORM_DESCRIPTOR_MIN_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %u/%d", + dev_cap->bLength, LIBUSB_BT_PLATFORM_DESCRIPTOR_MIN_SIZE); + return LIBUSB_ERROR_IO; + } + + _platform_descriptor = malloc(dev_cap->bLength); + if (!_platform_descriptor) + return LIBUSB_ERROR_NO_MEM; + + parse_descriptor(dev_cap, "bbbbu", _platform_descriptor); + + /* Capability data is located after reserved byte and 128-bit UUID */ + uint8_t* capability_data = dev_cap->dev_capability_data + 1 + 16; + + /* Capability data length is total descriptor length minus initial fields */ + size_t capability_data_length = _platform_descriptor->bLength - (16 + 4); + + memcpy(_platform_descriptor->CapabilityData, capability_data, capability_data_length); + + *platform_descriptor = _platform_descriptor; + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_desc + * Free a platform descriptor obtained from + * libusb_get_platform_descriptor(). + * It is safe to call this function with a NULL platform_descriptor parameter, + * in which case the function simply returns. + * + * \param platform_descriptor the Platform descriptor to free + */ +void API_EXPORTED libusb_free_platform_descriptor( + struct libusb_platform_descriptor *platform_descriptor) +{ + free(platform_descriptor); +} + /** \ingroup libusb_desc * Retrieve a string descriptor in C style ASCII. * @@ -1086,7 +1150,7 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_ha uint8_t desc_index, unsigned char *data, int length) { union usbi_string_desc_buf str; - int r, si, di; + int r; uint16_t langid, wdata; /* Asking for the zero'th index is special - it returns a string @@ -1122,18 +1186,214 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_ha else if ((str.desc.bLength & 1) || str.desc.bLength != r) usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor (read %d)", str.desc.bLength, r); - di = 0; - for (si = 2; si < str.desc.bLength; si += 2) { - if (di >= (length - 1)) - break; + /* Stop one byte before the end to leave room for null termination. */ + int dest_max = length - 1; + + /* The descriptor has this number of wide characters */ + int src_max = (str.desc.bLength - 1 - 1) / 2; - wdata = libusb_le16_to_cpu(str.desc.wData[di]); + /* Neither read nor write more than the smallest buffer */ + int idx_max = MIN(dest_max, src_max); + + int idx; + for (idx = 0; idx < idx_max; ++idx) { + wdata = libusb_le16_to_cpu(str.desc.wData[idx]); if (wdata < 0x80) - data[di++] = (unsigned char)wdata; + data[idx] = (unsigned char)wdata; else - data[di++] = '?'; /* non-ASCII */ + data[idx] = '?'; /* non-ASCII */ + } + + data[idx] = 0; /* null-terminate string */ + return idx; +} + +static int parse_iad_array(struct libusb_context *ctx, + struct libusb_interface_association_descriptor_array *iad_array, + const uint8_t *buffer, int size) +{ + uint8_t i; + struct usbi_descriptor_header header; + int consumed = 0; + const uint8_t *buf = buffer; + struct libusb_interface_association_descriptor *iad; + + if (size < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(ctx, "short config descriptor read %d/%d", + size, LIBUSB_DT_CONFIG_SIZE); + return LIBUSB_ERROR_IO; + } + + // First pass: Iterate through desc list, count number of IADs + iad_array->length = 0; + while (consumed < size) { + parse_descriptor(buf, "bb", &header); + if (header.bLength < 2) { + usbi_err(ctx, "invalid descriptor bLength %d", + header.bLength); + return LIBUSB_ERROR_IO; + } + if (header.bDescriptorType == LIBUSB_DT_INTERFACE_ASSOCIATION) + iad_array->length++; + buf += header.bLength; + consumed += header.bLength; + } + + iad_array->iad = NULL; + if (iad_array->length > 0) { + iad = calloc(iad_array->length, sizeof(*iad)); + if (!iad) + return LIBUSB_ERROR_NO_MEM; + + iad_array->iad = iad; + + // Second pass: Iterate through desc list, fill IAD structures + consumed = 0; + i = 0; + while (consumed < size) { + parse_descriptor(buffer, "bb", &header); + if (header.bDescriptorType == LIBUSB_DT_INTERFACE_ASSOCIATION) + parse_descriptor(buffer, "bbbbbbbb", &iad[i++]); + buffer += header.bLength; + consumed += header.bLength; + } } - data[di] = 0; - return di; + return LIBUSB_SUCCESS; +} + +static int raw_desc_to_iad_array(struct libusb_context *ctx, const uint8_t *buf, + int size, struct libusb_interface_association_descriptor_array **iad_array) +{ + struct libusb_interface_association_descriptor_array *_iad_array + = calloc(1, sizeof(*_iad_array)); + int r; + + if (!_iad_array) + return LIBUSB_ERROR_NO_MEM; + + r = parse_iad_array(ctx, _iad_array, buf, size); + if (r < 0) { + usbi_err(ctx, "parse_iad_array failed with error %d", r); + free(_iad_array); + return r; + } + + *iad_array = _iad_array; + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_desc + * Get an array of interface association descriptors (IAD) for a given + * configuration. + * This is a non-blocking function which does not involve any requests being + * sent to the device. + * + * \param dev a device + * \param config_index the index of the configuration you wish to retrieve the + * IADs for. + * \param iad_array output location for the array of IADs. Only valid if 0 was + * returned. Must be freed with libusb_free_interface_association_descriptors() + * after use. It's possible that a given configuration contains no IADs. In this + * case the iad_array is still output, but will have 'length' field set to 0, and + * iad field set to NULL. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns another LIBUSB_ERROR code on error + * \see libusb_get_active_interface_association_descriptors() + */ +int API_EXPORTED libusb_get_interface_association_descriptors(libusb_device *dev, + uint8_t config_index, struct libusb_interface_association_descriptor_array **iad_array) +{ + union usbi_config_desc_buf _config; + uint16_t config_len; + uint8_t *buf; + int r; + + if (!iad_array) + return LIBUSB_ERROR_INVALID_PARAM; + + usbi_dbg(DEVICE_CTX(dev), "IADs for config index %u", config_index); + if (config_index >= dev->device_descriptor.bNumConfigurations) + return LIBUSB_ERROR_NOT_FOUND; + + r = get_config_descriptor(dev, config_index, _config.buf, sizeof(_config.buf)); + if (r < 0) + return r; + + config_len = libusb_le16_to_cpu(_config.desc.wTotalLength); + buf = malloc(config_len); + if (!buf) + return LIBUSB_ERROR_NO_MEM; + + r = get_config_descriptor(dev, config_index, buf, config_len); + if (r >= 0) + r = raw_desc_to_iad_array(DEVICE_CTX(dev), buf, r, iad_array); + + free(buf); + return r; +} + +/** \ingroup libusb_desc + * Get an array of interface association descriptors (IAD) for the currently + * active configuration. + * This is a non-blocking function which does not involve any requests being + * sent to the device. + * + * \param dev a device + * \param iad_array output location for the array of IADs. Only valid if 0 was + * returned. Must be freed with libusb_free_interface_association_descriptors() + * after use. It's possible that a given configuration contains no IADs. In this + * case the iad_array is still output, but will have 'length' field set to 0, and + * iad field set to NULL. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state + * \returns another LIBUSB_ERROR code on error + * \see libusb_get_interface_association_descriptors + */ +int API_EXPORTED libusb_get_active_interface_association_descriptors(libusb_device *dev, + struct libusb_interface_association_descriptor_array **iad_array) +{ + union usbi_config_desc_buf _config; + uint16_t config_len; + uint8_t *buf; + int r; + + if (!iad_array) + return LIBUSB_ERROR_INVALID_PARAM; + + r = get_active_config_descriptor(dev, _config.buf, sizeof(_config.buf)); + if (r < 0) + return r; + + config_len = libusb_le16_to_cpu(_config.desc.wTotalLength); + buf = malloc(config_len); + if (!buf) + return LIBUSB_ERROR_NO_MEM; + + r = get_active_config_descriptor(dev, buf, config_len); + if (r >= 0) + r = raw_desc_to_iad_array(DEVICE_CTX(dev), buf, r, iad_array); + free(buf); + return r; +} + +/** \ingroup libusb_desc + * Free an array of interface association descriptors (IADs) obtained from + * libusb_get_interface_association_descriptors() or + * libusb_get_active_interface_association_descriptors(). + * It is safe to call this function with a NULL iad_array parameter, in which + * case the function simply returns. + * + * \param iad_array the IAD array to free + */ +void API_EXPORTED libusb_free_interface_association_descriptors( + struct libusb_interface_association_descriptor_array *iad_array) +{ + if (!iad_array) + return; + + if (iad_array->iad) + free((void*)iad_array->iad); + free(iad_array); } diff --git a/mac/libusb/hotplug.c b/mac/libusb/hotplug.c index 6b743c704..3c64f6919 100644 --- a/mac/libusb/hotplug.c +++ b/mac/libusb/hotplug.c @@ -33,7 +33,7 @@ * * \section hotplug_intro Introduction * - * Version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, has added support + * Version 1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102, has added support * for hotplug events on some platforms (you should test if your platform * supports hotplug notification by calling \ref libusb_has_capability() with * parameter \ref LIBUSB_CAP_HAS_HOTPLUG). @@ -117,7 +117,7 @@ int main (void) { libusb_hotplug_callback_handle callback_handle; int rc; - libusb_init(NULL); + libusb_init_context(NULL, NULL, 0); rc = libusb_hotplug_register_callback(NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, 0x045a, 0x5005, @@ -311,7 +311,7 @@ void usbi_hotplug_process(struct libusb_context *ctx, struct list_head *hotplug_ for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) { if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) { usbi_dbg(ctx, "freeing hotplug cb %p with handle %d", - hotplug_cb, hotplug_cb->handle); + (void *) hotplug_cb, hotplug_cb->handle); list_del(&hotplug_cb->list); free(hotplug_cb); } @@ -377,7 +377,8 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx, usbi_mutex_unlock(&ctx->hotplug_cbs_lock); - usbi_dbg(ctx, "new hotplug cb %p with handle %d", hotplug_cb, hotplug_cb->handle); + usbi_dbg(ctx, "new hotplug cb %p with handle %d", + (void *) hotplug_cb, hotplug_cb->handle); if ((flags & LIBUSB_HOTPLUG_ENUMERATE) && (events & LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)) { ssize_t i, len; diff --git a/mac/libusb/io.c b/mac/libusb/io.c index 9e3146cf9..ab84ba620 100644 --- a/mac/libusb/io.c +++ b/mac/libusb/io.c @@ -3,8 +3,8 @@ * I/O functions for libusb * Copyright © 2007-2009 Daniel Drake * Copyright © 2001 Johannes Erdfelt - * Copyright © 2019 Nathan Hjelm - * Copyright © 2019 Google LLC. All rights reserved. + * Copyright © 2019-2022 Nathan Hjelm + * Copyright © 2019-2022 Google LLC. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -313,6 +313,10 @@ if (r == 0 && actual_length == sizeof(data)) { * be invoked, and the callback function should check the transfer status to * determine that it was cancelled. * + * On macOS and iOS it is not possible to cancel a single transfer. In this + * case cancelling one transfer on an endpoint will cause all transfers on + * that endpoint to be cancelled. + * * Freeing the transfer after it has been cancelled but before cancellation * has completed will result in undefined behaviour. * @@ -1240,8 +1244,8 @@ void usbi_io_exit(struct libusb_context *ctx) static void calculate_timeout(struct usbi_transfer *itransfer) { - unsigned int timeout = - USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout; + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + unsigned int timeout = transfer->timeout; if (!timeout) { TIMESPEC_CLEAR(&itransfer->timeout); @@ -1285,30 +1289,25 @@ DEFAULT_VISIBILITY struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer( int iso_packets) { - size_t priv_size; - size_t alloc_size; - unsigned char *ptr; - struct usbi_transfer *itransfer; - struct libusb_transfer *transfer; - assert(iso_packets >= 0); if (iso_packets < 0) return NULL; - priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size); - alloc_size = priv_size - + sizeof(struct usbi_transfer) - + sizeof(struct libusb_transfer) - + (sizeof(struct libusb_iso_packet_descriptor) * (size_t)iso_packets); - ptr = calloc(1, alloc_size); + size_t priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size); + size_t usbi_transfer_size = PTR_ALIGN(sizeof(struct usbi_transfer)); + size_t libusb_transfer_size = PTR_ALIGN(sizeof(struct libusb_transfer)); + size_t iso_packets_size = sizeof(struct libusb_iso_packet_descriptor) * (size_t)iso_packets; + size_t alloc_size = priv_size + usbi_transfer_size + libusb_transfer_size + iso_packets_size; + unsigned char *ptr = calloc(1, alloc_size); if (!ptr) return NULL; - itransfer = (struct usbi_transfer *)(ptr + priv_size); + struct usbi_transfer *itransfer = (struct usbi_transfer *)(ptr + priv_size); itransfer->num_iso_packets = iso_packets; itransfer->priv = ptr; usbi_mutex_init(&itransfer->lock); - transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + return transfer; } @@ -1331,31 +1330,26 @@ struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer( */ void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer) { - struct usbi_transfer *itransfer; - size_t priv_size; - unsigned char *ptr; - if (!transfer) return; - usbi_dbg(TRANSFER_CTX(transfer), "transfer %p", transfer); + usbi_dbg(TRANSFER_CTX(transfer), "transfer %p", (void *) transfer); if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER) free(transfer->buffer); - itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); + struct usbi_transfer *itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); usbi_mutex_destroy(&itransfer->lock); if (itransfer->dev) libusb_unref_device(itransfer->dev); - priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size); - ptr = (unsigned char *)itransfer - priv_size; + unsigned char *ptr = USBI_TRANSFER_TO_TRANSFER_PRIV(itransfer); assert(ptr == itransfer->priv); free(ptr); } /* iterates through the flying transfers, and rearms the timer based on the * next upcoming timeout. - * must be called with flying_list locked. + * NB: flying_transfers_lock must be held when calling this. * returns 0 on success or a LIBUSB_ERROR code on failure. */ #ifdef HAVE_OS_TIMER @@ -1376,7 +1370,8 @@ static int arm_timer_for_next_timeout(struct libusb_context *ctx) /* act on first transfer that has not already been handled */ if (!(itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) { - usbi_dbg(ctx, "next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout); + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + usbi_dbg(ctx, "next timeout originally %ums", transfer->timeout); return usbi_arm_timer(&ctx->timer, cur_ts); } } @@ -1394,7 +1389,8 @@ static inline int arm_timer_for_next_timeout(struct libusb_context *ctx) /* add a transfer to the (timeout-sorted) active transfers list. * This function will return non 0 if fails to update the timer, - * in which case the transfer is *not* on the flying_transfers list. */ + * in which case the transfer is *not* on the flying_transfers list. + * NB: flying_transfers_lock MUST be held when calling this. */ static int add_to_flying_list(struct usbi_transfer *itransfer) { struct usbi_transfer *cur; @@ -1438,8 +1434,9 @@ out: if (first && usbi_using_timer(ctx) && TIMESPEC_IS_SET(timeout)) { /* if this transfer has the lowest timeout of all active transfers, * rearm the timer with this transfer's timeout */ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); usbi_dbg(ctx, "arm timer for timeout in %ums (first in line)", - USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout); + transfer->timeout); r = usbi_arm_timer(&ctx->timer, timeout); } #else @@ -1455,20 +1452,19 @@ out: /* remove a transfer from the active transfers list. * This function will *always* remove the transfer from the * flying_transfers list. It will return a LIBUSB_ERROR code - * if it fails to update the timer for the next timeout. */ + * if it fails to update the timer for the next timeout. + * NB: flying_transfers_lock MUST be held when calling this. */ static int remove_from_flying_list(struct usbi_transfer *itransfer) { struct libusb_context *ctx = ITRANSFER_CTX(itransfer); int rearm_timer; int r = 0; - usbi_mutex_lock(&ctx->flying_transfers_lock); rearm_timer = (TIMESPEC_IS_SET(&itransfer->timeout) && list_first_entry(&ctx->flying_transfers, struct usbi_transfer, list) == itransfer); list_del(&itransfer->list); if (rearm_timer) r = arm_timer_for_next_timeout(ctx); - usbi_mutex_unlock(&ctx->flying_transfers_lock); return r; } @@ -1479,11 +1475,11 @@ static int remove_from_flying_list(struct usbi_transfer *itransfer) * * \param transfer the transfer to submit * \returns 0 on success - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - * \returns LIBUSB_ERROR_BUSY if the transfer has already been submitted. - * \returns LIBUSB_ERROR_NOT_SUPPORTED if the transfer flags are not supported + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_BUSY if the transfer has already been submitted. + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if the transfer flags are not supported * by the operating system. - * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than * the operating system and/or hardware can support (see \ref asynclimits) * \returns another LIBUSB_ERROR code on other failure */ @@ -1500,7 +1496,7 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) itransfer->dev = libusb_ref_device(transfer->dev_handle->dev); ctx = HANDLE_CTX(transfer->dev_handle); - usbi_dbg(ctx, "transfer %p", transfer); + usbi_dbg(ctx, "transfer %p", (void *) transfer); /* * Important note on locking, this function takes / releases locks @@ -1562,8 +1558,11 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) } usbi_mutex_unlock(&itransfer->lock); - if (r != LIBUSB_SUCCESS) + if (r != LIBUSB_SUCCESS) { + usbi_mutex_lock(&ctx->flying_transfers_lock); remove_from_flying_list(itransfer); + usbi_mutex_unlock(&ctx->flying_transfers_lock); + } return r; } @@ -1584,21 +1583,23 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) * \ref libusb_transfer_status::LIBUSB_TRANSFER_CANCELLED * "LIBUSB_TRANSFER_CANCELLED" for each transfer that was cancelled. - * - Calling this function also sends a \c ClearFeature(ENDPOINT_HALT) request - * for the transfer's endpoint. If the device does not handle this request - * correctly, the data toggle bits for the endpoint can be left out of sync - * between host and device, which can have unpredictable results when the - * next data is sent on the endpoint, including data being silently lost. - * A call to \ref libusb_clear_halt will not resolve this situation, since - * that function uses the same request. Therefore, if your program runs on - * Darwin and uses a device that does not correctly implement - * \c ClearFeature(ENDPOINT_HALT) requests, it may only be safe to cancel - * transfers when followed by a device reset using + * - When built for macOS versions prior to 10.5, this function sends a + * \c ClearFeature(ENDPOINT_HALT) request for the transfer's endpoint. + * (Prior to libusb 1.0.27, this request was sent on all Darwin systems.) + * If the device does not handle this request correctly, the data toggle + * bits for the endpoint can be left out of sync between host and device, + * which can have unpredictable results when the next data is sent on + * the endpoint, including data being silently lost. A call to + * \ref libusb_clear_halt will not resolve this situation, since that + * function uses the same request. Therefore, if your program runs on + * macOS < 10.5 (or libusb < 1.0.27), and uses a device that does not + * correctly implement \c ClearFeature(ENDPOINT_HALT) requests, it may + * only be safe to cancel transfers when followed by a device reset using * \ref libusb_reset_device. * * \param transfer the transfer to cancel * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the transfer is not in progress, + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the transfer is not in progress, * already complete, or already cancelled. * \returns a LIBUSB_ERROR code on failure */ @@ -1609,7 +1610,7 @@ int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer) struct libusb_context *ctx = ITRANSFER_CTX(itransfer); int r; - usbi_dbg(ctx, "transfer %p", transfer ); + usbi_dbg(ctx, "transfer %p", (void *) transfer ); usbi_mutex_lock(&itransfer->lock); if (!(itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT) || (itransfer->state_flags & USBI_TRANSFER_CANCELLING)) { @@ -1689,7 +1690,9 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, uint8_t flags; int r; + usbi_mutex_lock(&ctx->flying_transfers_lock); r = remove_from_flying_list(itransfer); + usbi_mutex_unlock(&ctx->flying_transfers_lock); if (r < 0) usbi_err(ctx, "failed to set timer for next timeout"); @@ -1711,9 +1714,13 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, flags = transfer->flags; transfer->status = status; transfer->actual_length = itransfer->transferred; - usbi_dbg(ctx, "transfer %p has callback %p", transfer, transfer->callback); - if (transfer->callback) + usbi_dbg(ctx, "transfer %p has callback %p", + (void *) transfer, transfer->callback); + if (transfer->callback) { + libusb_lock_event_waiters (ctx); transfer->callback(transfer); + libusb_unlock_event_waiters(ctx); + } /* transfer might have been freed by the above call, do not use from * this point. */ if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) @@ -2013,7 +2020,7 @@ void API_EXPORTED libusb_unlock_event_waiters(libusb_context *ctx) * indicates unlimited timeout. * \returns 0 after a transfer completes or another thread stops event handling * \returns 1 if the timeout expired - * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if timeval is invalid * \ref libusb_mtasync */ int API_EXPORTED libusb_wait_for_event(libusb_context *ctx, struct timeval *tv) @@ -2037,6 +2044,7 @@ int API_EXPORTED libusb_wait_for_event(libusb_context *ctx, struct timeval *tv) return 0; } +// NB: flying_transfers_lock must be held when calling this static void handle_timeout(struct usbi_transfer *itransfer) { struct libusb_transfer *transfer = @@ -2052,6 +2060,7 @@ static void handle_timeout(struct usbi_transfer *itransfer) "async cancel failed %d", r); } +// NB: flying_transfers_lock must be held when calling this static void handle_timeouts_locked(struct libusb_context *ctx) { struct timespec systime; @@ -2332,7 +2341,7 @@ static int get_next_timeout(libusb_context *ctx, struct timeval *tv, * timeval struct for non-blocking mode * \param completed pointer to completion integer to check, or NULL * \returns 0 on success - * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if timeval is invalid * \returns another LIBUSB_ERROR code on other failure * \ref libusb_mtasync */ @@ -2474,7 +2483,7 @@ int API_EXPORTED libusb_handle_events_completed(libusb_context *ctx, * \param tv the maximum time to block waiting for events, or zero for * non-blocking mode * \returns 0 on success - * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if timeval is invalid * \returns another LIBUSB_ERROR code on other failure * \ref libusb_mtasync */ @@ -2558,7 +2567,7 @@ int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx) * \param tv output location for a relative time against the current * clock in which libusb must be called into in order to process timeout events * \returns 0 if there are no pending timeouts, 1 if a timeout was returned, - * or LIBUSB_ERROR_OTHER on failure + * or \ref LIBUSB_ERROR_OTHER on failure */ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx, struct timeval *tv) @@ -2619,11 +2628,11 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx, * To remove notifiers, pass NULL values for the function pointers. * * Note that file descriptors may have been added even before you register - * these notifiers (e.g. at libusb_init() time). + * these notifiers (e.g. at libusb_init_context() time). * * Additionally, note that the removal notifier may be called during * libusb_exit() (e.g. when it is closing file descriptors that were opened - * and added to the poll set at libusb_init() time). If you don't want this, + * and added to the poll set at libusb_init_context() time). If you don't want this, * remove the notifiers immediately before calling libusb_exit(). * * \param ctx the context to operate on, or NULL for the default context @@ -2827,7 +2836,8 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle) to_cancel = NULL; usbi_mutex_lock(&ctx->flying_transfers_lock); for_each_transfer(ctx, cur) { - if (USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur)->dev_handle == dev_handle) { + struct libusb_transfer *cur_transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur); + if (cur_transfer->dev_handle == dev_handle) { usbi_mutex_lock(&cur->lock); if (cur->state_flags & USBI_TRANSFER_IN_FLIGHT) to_cancel = cur; @@ -2842,8 +2852,9 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle) if (!to_cancel) break; + struct libusb_transfer *transfer_to_cancel = USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel); usbi_dbg(ctx, "cancelling transfer %p from disconnect", - USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel)); + (void *) transfer_to_cancel); usbi_mutex_lock(&to_cancel->lock); usbi_backend.clear_transfer_priv(to_cancel); diff --git a/mac/libusb/libusb.h b/mac/libusb/libusb.h index 2592ea779..f4e9203c6 100644 --- a/mac/libusb/libusb.h +++ b/mac/libusb/libusb.h @@ -3,9 +3,9 @@ * Copyright © 2001 Johannes Erdfelt * Copyright © 2007-2008 Daniel Drake * Copyright © 2012 Pete Batard - * Copyright © 2012-2018 Nathan Hjelm + * Copyright © 2012-2023 Nathan Hjelm * Copyright © 2014-2020 Chris Dickens - * For more information, please visit: http://libusb.info + * For more information, please visit: https://libusb.info * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -50,9 +50,9 @@ typedef SSIZE_T ssize_t; #include #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) -#define ZERO_SIZED_ARRAY /* [] - valid C99 code */ +#define LIBUSB_FLEXIBLE_ARRAY /* [] - valid C99 code */ #else -#define ZERO_SIZED_ARRAY 0 /* [0] - non-standard, but usually working code */ +#define LIBUSB_FLEXIBLE_ARRAY 0 /* [0] - non-standard, but usually working code */ #endif /* __STDC_VERSION__ */ /* 'interface' might be defined as a macro on Windows, so we need to @@ -74,6 +74,8 @@ typedef SSIZE_T ssize_t; #define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated ("Use " #f " instead"))) #elif defined(__GNUC__) && (__GNUC__ >= 3) #define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated)) +#elif defined(_MSC_VER) +#define LIBUSB_DEPRECATED_FOR(f) __declspec(deprecated("Use " #f " instead")) #else #define LIBUSB_DEPRECATED_FOR(f) #endif /* __GNUC__ */ @@ -118,20 +120,25 @@ typedef SSIZE_T ssize_t; */ #if defined(_WIN32) || defined(__CYGWIN__) #define LIBUSB_CALL WINAPI +#define LIBUSB_CALLV WINAPIV #else #define LIBUSB_CALL +#define LIBUSB_CALLV #endif /* _WIN32 || __CYGWIN__ */ /** \def LIBUSB_API_VERSION * \ingroup libusb_misc * libusb's API version. * - * Since version 1.0.13, to help with feature detection, libusb defines + * Since version 1.0.18, to help with feature detection, libusb defines * a LIBUSB_API_VERSION macro that gets increased every time there is a * significant change to the API, such as the introduction of a new call, * the definition of a new macro/enum member, or any other element that * libusb applications may want to detect at compilation time. * + * Between versions 1.0.13 and 1.0.17 (inclusive) the older spelling of + * LIBUSBX_API_VERSION was used. + * * The macro is typically used in an application as follows: * \code * #if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01001234) @@ -141,10 +148,34 @@ typedef SSIZE_T ssize_t; * * Internally, LIBUSB_API_VERSION is defined as follows: * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental) + * + * The incremental component has changed as follows: + *
    + *
  • libusbx version 1.0.13: LIBUSBX_API_VERSION = 0x01000100 + *
  • libusbx version 1.0.14: LIBUSBX_API_VERSION = 0x010000FF + *
  • libusbx version 1.0.15: LIBUSBX_API_VERSION = 0x01000101 + *
  • libusbx version 1.0.16: LIBUSBX_API_VERSION = 0x01000102 + *
  • libusbx version 1.0.17: LIBUSBX_API_VERSION = 0x01000102 + *
  • libusb version 1.0.18: LIBUSB_API_VERSION = 0x01000102 + *
  • libusb version 1.0.19: LIBUSB_API_VERSION = 0x01000103 + *
  • libusb version 1.0.20: LIBUSB_API_VERSION = 0x01000104 + *
  • libusb version 1.0.21: LIBUSB_API_VERSION = 0x01000105 + *
  • libusb version 1.0.22: LIBUSB_API_VERSION = 0x01000106 + *
  • libusb version 1.0.23: LIBUSB_API_VERSION = 0x01000107 + *
  • libusb version 1.0.24: LIBUSB_API_VERSION = 0x01000108 + *
  • libusb version 1.0.25: LIBUSB_API_VERSION = 0x01000109 + *
  • libusb version 1.0.26: LIBUSB_API_VERSION = 0x01000109 + *
  • libusb version 1.0.27: LIBUSB_API_VERSION = 0x0100010A + *
*/ -#define LIBUSB_API_VERSION 0x01000109 +#define LIBUSB_API_VERSION 0x0100010A -/* The following is kept for compatibility, but will be deprecated in the future */ +/** \def LIBUSBX_API_VERSION + * \ingroup libusb_misc + * + * This is the older spelling, kept for backwards compatibility of code + * needing to test for older library versions where the newer spelling + * did not exist. */ #define LIBUSBX_API_VERSION LIBUSB_API_VERSION #if defined(__cplusplus) @@ -265,6 +296,10 @@ enum libusb_descriptor_type { /** Endpoint descriptor. See libusb_endpoint_descriptor. */ LIBUSB_DT_ENDPOINT = 0x05, + /** Interface Association Descriptor. + * See libusb_interface_association_descriptor */ + LIBUSB_DT_INTERFACE_ASSOCIATION = 0x0b, + /** BOS descriptor */ LIBUSB_DT_BOS = 0x0f, @@ -305,6 +340,7 @@ enum libusb_descriptor_type { #define LIBUSB_BT_USB_2_0_EXTENSION_SIZE 7 #define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE 10 #define LIBUSB_BT_CONTAINER_ID_SIZE 20 +#define LIBUSB_BT_PLATFORM_DESCRIPTOR_MIN_SIZE 20 /* We unwrap the BOS => define its max size */ #define LIBUSB_DT_BOS_MAX_SIZE \ @@ -523,7 +559,10 @@ enum libusb_bos_type { LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 0x03, /** Container ID type */ - LIBUSB_BT_CONTAINER_ID = 0x04 + LIBUSB_BT_CONTAINER_ID = 0x04, + + /** Platform descriptor */ + LIBUSB_BT_PLATFORM_DESCRIPTOR = 0x05 }; /** \ingroup libusb_desc @@ -628,6 +667,65 @@ struct libusb_endpoint_descriptor { int extra_length; }; +/** \ingroup libusb_desc + * A structure representing the standard USB interface association descriptor. + * This descriptor is documented in section 9.6.4 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_interface_association_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE_ASSOCIATION + * LIBUSB_DT_INTERFACE_ASSOCIATION in this context. */ + uint8_t bDescriptorType; + + /** Interface number of the first interface that is associated + * with this function */ + uint8_t bFirstInterface; + + /** Number of contiguous interfaces that are associated with + * this function */ + uint8_t bInterfaceCount; + + /** USB-IF class code for this function. + * A value of zero is not allowed in this descriptor. + * If this field is 0xff, the function class is vendor-specific. + * All other values are reserved for assignment by the USB-IF. + */ + uint8_t bFunctionClass; + + /** USB-IF subclass code for this function. + * If this field is not set to 0xff, all values are reserved + * for assignment by the USB-IF + */ + uint8_t bFunctionSubClass; + + /** USB-IF protocol code for this function. + * These codes are qualified by the values of the bFunctionClass + * and bFunctionSubClass fields. + */ + uint8_t bFunctionProtocol; + + /** Index of string descriptor describing this function */ + uint8_t iFunction; +}; + +/** \ingroup libusb_desc + * Structure containing an array of 0 or more interface association + * descriptors + */ +struct libusb_interface_association_descriptor_array { + /** Array of interface association descriptors. The size of this array + * is determined by the length field. + */ + const struct libusb_interface_association_descriptor *iad; + + /** Number of interface association descriptors contained. Read-only. */ + int length; +}; + /** \ingroup libusb_desc * A structure representing the standard USB interface descriptor. This * descriptor is documented in section 9.6.5 of the USB 3.0 specification. @@ -786,7 +884,7 @@ struct libusb_bos_dev_capability_descriptor { uint8_t bDevCapabilityType; /** Device Capability data (bLength - 3 bytes) */ - uint8_t dev_capability_data[ZERO_SIZED_ARRAY]; + uint8_t dev_capability_data[LIBUSB_FLEXIBLE_ARRAY]; }; /** \ingroup libusb_desc @@ -811,7 +909,7 @@ struct libusb_bos_descriptor { uint8_t bNumDeviceCaps; /** bNumDeviceCap Device Capability Descriptors */ - struct libusb_bos_dev_capability_descriptor *dev_capability[ZERO_SIZED_ARRAY]; + struct libusb_bos_dev_capability_descriptor *dev_capability[LIBUSB_FLEXIBLE_ARRAY]; }; /** \ingroup libusb_desc @@ -908,6 +1006,34 @@ struct libusb_container_id_descriptor { uint8_t ContainerID[16]; }; +/** \ingroup libusb_desc + * A structure representing a Platform descriptor. + * This descriptor is documented in section 9.6.2.4 of the USB 3.2 specification. + */ +struct libusb_platform_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_PLATFORM_DESCRIPTOR + * LIBUSB_BT_CONTAINER_ID in this context. */ + uint8_t bDevCapabilityType; + + /** Reserved field */ + uint8_t bReserved; + + /** 128 bit UUID */ + uint8_t PlatformCapabilityUUID[16]; + + /** Capability data (bLength - 20) */ + uint8_t CapabilityData[LIBUSB_FLEXIBLE_ARRAY]; +}; + /** \ingroup libusb_asyncio * Setup packet for control transfers. */ #if defined(_MSC_VER) || defined(__WATCOMC__) @@ -982,7 +1108,7 @@ struct libusb_version { * libusb_exit() will not destroy resources that the other user is still * using. * - * Sessions are created by libusb_init() and destroyed through libusb_exit(). + * Sessions are created by libusb_init_context() and destroyed through libusb_exit(). * If your application is guaranteed to only ever include a single libusb * user (i.e. you), you do not have to worry about contexts: pass NULL in * every function call where a context is required, and the default context @@ -1187,7 +1313,8 @@ enum libusb_transfer_flags { * * This flag is currently only supported on Linux. * On other systems, libusb_submit_transfer() will return - * LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this flag is set. + * \ref LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this + * flag is set. * * Available since libusb-1.0.9. */ @@ -1284,7 +1411,7 @@ struct libusb_transfer { int num_iso_packets; /** Isochronous packet descriptors, for isochronous transfers only. */ - struct libusb_iso_packet_descriptor iso_packet_desc[ZERO_SIZED_ARRAY]; + struct libusb_iso_packet_descriptor iso_packet_desc[LIBUSB_FLEXIBLE_ARRAY]; }; /** \ingroup libusb_misc @@ -1345,6 +1472,79 @@ enum libusb_log_cb_mode { LIBUSB_LOG_CB_CONTEXT = (1 << 1) }; +/** \ingroup libusb_lib + * Available option values for libusb_set_option() and libusb_init_context(). + */ +enum libusb_option { + /** Set the log message verbosity. + * + * This option must be provided an argument of type \ref libusb_log_level. + * The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever + * printed. If you choose to increase the message verbosity level, ensure + * that your application does not close the stderr file descriptor. + * + * You are advised to use level LIBUSB_LOG_LEVEL_WARNING. libusb is conservative + * with its message logging and most of the time, will only log messages that + * explain error conditions and other oddities. This will help you debug + * your software. + * + * If the LIBUSB_DEBUG environment variable was set when libusb was + * initialized, this option does nothing: the message verbosity is fixed + * to the value in the environment variable. + * + * If libusb was compiled without any message logging, this option does + * nothing: you'll never get any messages. + * + * If libusb was compiled with verbose debug message logging, this option + * does nothing: you'll always get messages from all levels. + */ + LIBUSB_OPTION_LOG_LEVEL = 0, + + /** Use the UsbDk backend for a specific context, if available. + * + * This option should be set at initialization with libusb_init_context() + * otherwise unspecified behavior may occur. + * + * Only valid on Windows. Ignored on all other platforms. + */ + LIBUSB_OPTION_USE_USBDK = 1, + + /** Do not scan for devices + * + * With this option set, libusb will skip scanning devices in + * libusb_init_context(). + * + * Hotplug functionality will also be deactivated. + * + * The option is useful in combination with libusb_wrap_sys_device(), + * which can access a device directly without prior device scanning. + * + * This is typically needed on Android, where access to USB devices + * is limited. + * + * This option should only be used with libusb_init_context() + * otherwise unspecified behavior may occur. + * + * Only valid on Linux. Ignored on all other platforms. + */ + LIBUSB_OPTION_NO_DEVICE_DISCOVERY = 2, + +#define LIBUSB_OPTION_WEAK_AUTHORITY LIBUSB_OPTION_NO_DEVICE_DISCOVERY + + /** Set the context log callback function. + * + * Set the log callback function either on a context or globally. This + * option must be provided an argument of type \ref libusb_log_cb. + * Using this option with a NULL context is equivalent to calling + * libusb_set_log_cb() with mode \ref LIBUSB_LOG_CB_GLOBAL. + * Using it with a non-NULL context is equivalent to calling + * libusb_set_log_cb() with mode \ref LIBUSB_LOG_CB_CONTEXT. + */ + LIBUSB_OPTION_LOG_CB = 3, + + LIBUSB_OPTION_MAX = 4 +}; + /** \ingroup libusb_lib * Callback function for handling log messages. * \param ctx the context which is related to the log message, or NULL if it @@ -1359,10 +1559,25 @@ enum libusb_log_cb_mode { typedef void (LIBUSB_CALL *libusb_log_cb)(libusb_context *ctx, enum libusb_log_level level, const char *str); +/** \ingroup libusb_lib + * Structure used for setting options through \ref libusb_init_context. + * + */ +struct libusb_init_option { + /** Which option to set */ + enum libusb_option option; + /** An integer value used by the option (if applicable). */ + union { + int ival; + libusb_log_cb log_cbval; + } value; +}; + int LIBUSB_CALL libusb_init(libusb_context **ctx); +int LIBUSB_CALL libusb_init_context(libusb_context **ctx, const struct libusb_init_option options[], int num_options); void LIBUSB_CALL libusb_exit(libusb_context *ctx); -LIBUSB_DEPRECATED_FOR(libusb_set_option) void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); +/* may be deprecated in the future in favor of lubusb_init_context()+libusb_set_option() */ void LIBUSB_CALL libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, int mode); const struct libusb_version * LIBUSB_CALL libusb_get_version(void); int LIBUSB_CALL libusb_has_capability(uint32_t capability); @@ -1415,6 +1630,11 @@ int LIBUSB_CALL libusb_get_container_id_descriptor(libusb_context *ctx, struct libusb_container_id_descriptor **container_id); void LIBUSB_CALL libusb_free_container_id_descriptor( struct libusb_container_id_descriptor *container_id); +int LIBUSB_CALL libusb_get_platform_descriptor(libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_platform_descriptor **platform_descriptor); +void LIBUSB_CALL libusb_free_platform_descriptor( + struct libusb_platform_descriptor *platform_descriptor); uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev); uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev); int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t *port_numbers, int port_numbers_len); @@ -1427,6 +1647,15 @@ int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev, unsigned char endpoint); int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev, unsigned char endpoint); +int LIBUSB_CALL libusb_get_max_alt_packet_size(libusb_device *dev, + int interface_number, int alternate_setting, unsigned char endpoint); + +int LIBUSB_CALL libusb_get_interface_association_descriptors(libusb_device *dev, + uint8_t config_index, struct libusb_interface_association_descriptor_array **iad_array); +int LIBUSB_CALL libusb_get_active_interface_association_descriptors(libusb_device *dev, + struct libusb_interface_association_descriptor_array **iad_array); +void LIBUSB_CALL libusb_free_interface_association_descriptors( + struct libusb_interface_association_descriptor_array *iad_array); int LIBUSB_CALL libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, libusb_device_handle **dev_handle); int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **dev_handle); @@ -2036,7 +2265,7 @@ typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx, * \param[in] cb_fn the function to be invoked on a matching event/device * \param[in] user_data user data to pass to the callback function * \param[out] callback_handle pointer to store the handle of the allocated callback (can be NULL) - * \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure + * \returns \ref LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure */ int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx, int events, int flags, @@ -2069,67 +2298,7 @@ void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx, void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx, libusb_hotplug_callback_handle callback_handle); -/** \ingroup libusb_lib - * Available option values for libusb_set_option(). - */ -enum libusb_option { - /** Set the log message verbosity. - * - * The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever - * printed. If you choose to increase the message verbosity level, ensure - * that your application does not close the stderr file descriptor. - * - * You are advised to use level LIBUSB_LOG_LEVEL_WARNING. libusb is conservative - * with its message logging and most of the time, will only log messages that - * explain error conditions and other oddities. This will help you debug - * your software. - * - * If the LIBUSB_DEBUG environment variable was set when libusb was - * initialized, this function does nothing: the message verbosity is fixed - * to the value in the environment variable. - * - * If libusb was compiled without any message logging, this function does - * nothing: you'll never get any messages. - * - * If libusb was compiled with verbose debug message logging, this function - * does nothing: you'll always get messages from all levels. - */ - LIBUSB_OPTION_LOG_LEVEL = 0, - - /** Use the UsbDk backend for a specific context, if available. - * - * This option should be set immediately after calling libusb_init(), otherwise - * unspecified behavior may occur. - * - * Only valid on Windows. - */ - LIBUSB_OPTION_USE_USBDK = 1, - - /** Do not scan for devices - * - * With this option set, libusb will skip scanning devices in - * libusb_init(). Must be set before calling libusb_init(). - * - * Hotplug functionality will also be deactivated. - * - * The option is useful in combination with libusb_wrap_sys_device(), - * which can access a device directly without prior device scanning. - * - * This is typically needed on Android, where access to USB devices - * is limited. - * - * For LIBUSB_API_VERSION 0x01000108 it was called LIBUSB_OPTION_WEAK_AUTHORITY - * - * Only valid on Linux. - */ - LIBUSB_OPTION_NO_DEVICE_DISCOVERY = 2, - -#define LIBUSB_OPTION_WEAK_AUTHORITY LIBUSB_OPTION_NO_DEVICE_DISCOVERY - - LIBUSB_OPTION_MAX = 3 -}; - -int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...); +int LIBUSB_CALLV libusb_set_option(libusb_context *ctx, enum libusb_option option, ...); #ifdef _MSC_VER #pragma warning(pop) diff --git a/mac/libusb/libusbi.h b/mac/libusb/libusbi.h index b1fc88c99..3b0c6105c 100644 --- a/mac/libusb/libusbi.h +++ b/mac/libusb/libusbi.h @@ -73,7 +73,7 @@ #endif /* The following is used to silence warnings for unused variables */ -#if defined(UNREFERENCED_PARAMETER) +#if defined(UNREFERENCED_PARAMETER) && !defined(__GNUC__) #define UNUSED(var) UNREFERENCED_PARAMETER(var) #else #define UNUSED(var) do { (void)(var); } while(0) @@ -128,6 +128,7 @@ typedef atomic_long usbi_atomic_t; * return_type LIBUSB_CALL function_name(params); */ #define API_EXPORTED LIBUSB_CALL DEFAULT_VISIBILITY +#define API_EXPORTEDV LIBUSB_CALLV DEFAULT_VISIBILITY #ifdef __cplusplus extern "C" { @@ -321,10 +322,10 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, #else /* ENABLE_LOGGING */ -#define usbi_err(ctx, ...) UNUSED(ctx) -#define usbi_warn(ctx, ...) UNUSED(ctx) -#define usbi_info(ctx, ...) UNUSED(ctx) -#define usbi_dbg(ctx, ...) do {} while (0) +#define usbi_err(ctx, ...) do { (void)(ctx); } while(0) +#define usbi_warn(ctx, ...) do { (void)(ctx); } while(0) +#define usbi_info(ctx, ...) do { (void)(ctx); } while(0) +#define usbi_dbg(ctx, ...) do { (void)(ctx); } while(0) #endif /* ENABLE_LOGGING */ @@ -379,7 +380,7 @@ struct libusb_context { struct list_head flying_transfers; /* Note paths taking both this and usbi_transfer->lock must always * take this lock first */ - usbi_mutex_t flying_transfers_lock; + usbi_mutex_t flying_transfers_lock; /* for flying_transfers and timeout_flags */ #if !defined(PLATFORM_WINDOWS) /* user callbacks for pollfd changes */ @@ -533,7 +534,7 @@ static inline void usbi_localize_device_descriptor(struct libusb_device_descript desc->bcdDevice = libusb_le16_to_cpu(desc->bcdDevice); } -#ifdef HAVE_CLOCK_GETTIME +#if defined(HAVE_CLOCK_GETTIME) && !defined(__APPLE__) static inline void usbi_get_monotonic_time(struct timespec *tp) { ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, tp), 0); @@ -562,8 +563,11 @@ void usbi_get_real_time(struct timespec *tp); * 2. struct usbi_transfer * 3. struct libusb_transfer (which includes iso packets) [variable size] * - * from a libusb_transfer, you can get the usbi_transfer by rewinding the - * appropriate number of bytes. + * You can convert between them with the macros: + * TRANSFER_PRIV_TO_USBI_TRANSFER + * USBI_TRANSFER_TO_TRANSFER_PRIV + * USBI_TRANSFER_TO_LIBUSB_TRANSFER + * LIBUSB_TRANSFER_TO_USBI_TRANSFER */ struct usbi_transfer { @@ -574,7 +578,7 @@ struct usbi_transfer { int transferred; uint32_t stream_id; uint32_t state_flags; /* Protected by usbi_transfer->lock */ - uint32_t timeout_flags; /* Protected by the flying_stransfers_lock */ + uint32_t timeout_flags; /* Protected by the flying_transfers_lock */ /* The device reference is held until destruction for logging * even after dev_handle is set to NULL. */ @@ -616,10 +620,21 @@ enum usbi_transfer_timeout_flags { USBI_TRANSFER_TIMED_OUT = 1U << 2, }; +#define TRANSFER_PRIV_TO_USBI_TRANSFER(transfer_priv) \ + ((struct usbi_transfer *) \ + ((unsigned char *)(transfer_priv) \ + + PTR_ALIGN(sizeof(*transfer_priv)))) + +#define USBI_TRANSFER_TO_TRANSFER_PRIV(itransfer) \ + ((unsigned char *) \ + ((unsigned char *)(itransfer) \ + - PTR_ALIGN(usbi_backend.transfer_priv_size))) + #define USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer) \ ((struct libusb_transfer *) \ ((unsigned char *)(itransfer) \ + PTR_ALIGN(sizeof(struct usbi_transfer)))) + #define LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \ ((struct usbi_transfer *) \ ((unsigned char *)(transfer) \ @@ -678,7 +693,7 @@ struct usbi_interface_descriptor { struct usbi_string_descriptor { uint8_t bLength; uint8_t bDescriptorType; - uint16_t wData[ZERO_SIZED_ARRAY]; + uint16_t wData[LIBUSB_FLEXIBLE_ARRAY]; } LIBUSB_PACKED; struct usbi_bos_descriptor { @@ -813,6 +828,7 @@ struct usbi_option { int is_set; union { int ival; + libusb_log_cb log_cbval; } arg; }; @@ -891,7 +907,7 @@ static inline void *usbi_get_transfer_priv(struct usbi_transfer *itransfer) struct discovered_devs { size_t len; size_t capacity; - struct libusb_device *devices[ZERO_SIZED_ARRAY]; + struct libusb_device *devices[LIBUSB_FLEXIBLE_ARRAY]; }; struct discovered_devs *discovered_devs_append( @@ -1180,6 +1196,8 @@ struct usbi_os_backend { * claiming, no other drivers/applications can use the interface because * we now "own" it. * + * This function gets called with dev_handle->lock locked! + * * Return: * - 0 on success * - LIBUSB_ERROR_NOT_FOUND if the interface does not exist @@ -1199,6 +1217,8 @@ struct usbi_os_backend { * You will only ever be asked to release an interface which was * successfully claimed earlier. * + * This function gets called with dev_handle->lock locked! + * * Return: * - 0 on success * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it @@ -1335,7 +1355,7 @@ struct usbi_os_backend { * * This function must not block. * - * This function gets called with the flying_transfers_lock locked! + * This function gets called with itransfer->lock locked! * * Return: * - 0 on success @@ -1349,6 +1369,8 @@ struct usbi_os_backend { * This function must not block. The transfer cancellation must complete * later, resulting in a call to usbi_handle_transfer_cancellation() * from the context of handle_events. + * + * This function gets called with itransfer->lock locked! */ int (*cancel_transfer)(struct usbi_transfer *itransfer); diff --git a/mac/libusb/os/darwin_usb.c b/mac/libusb/os/darwin_usb.c index 388dbca60..c0963e09b 100644 --- a/mac/libusb/os/darwin_usb.c +++ b/mac/libusb/os/darwin_usb.c @@ -1,8 +1,8 @@ /* -*- Mode: C; indent-tabs-mode:nil -*- */ /* * darwin backend for libusb 1.0 - * Copyright © 2008-2021 Nathan Hjelm - * Copyright © 2019-2021 Google LLC. All rights reserved. + * Copyright © 2008-2023 Nathan Hjelm + * Copyright © 2019-2023 Google LLC. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -32,10 +32,7 @@ #include #include -#include -#include -#include -#include +#include /* Suppress warnings about the use of the deprecated objc_registerThreadWithCollector * function. Its use is also conditionalized to only older deployment targets. */ @@ -58,28 +55,34 @@ static int init_count = 0; static const mach_port_t darwin_default_master_port = 0; /* async event thread */ +/* if both this mutex and darwin_cached_devices_mutex are to be acquired then + darwin_cached_devices_mutex must be acquired first. */ static pthread_mutex_t libusb_darwin_at_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER; -#if !defined(HAVE_CLOCK_GETTIME) -static clock_serv_t clock_realtime; -static clock_serv_t clock_monotonic; -#endif - #define LIBUSB_DARWIN_STARTUP_FAILURE ((CFRunLoopRef) -1) static CFRunLoopRef libusb_darwin_acfl = NULL; /* event cf loop */ static CFRunLoopSourceRef libusb_darwin_acfls = NULL; /* shutdown signal for event cf loop */ -static usbi_mutex_t darwin_cached_devices_lock = PTHREAD_MUTEX_INITIALIZER; +static usbi_mutex_t darwin_cached_devices_mutex = PTHREAD_MUTEX_INITIALIZER; static struct list_head darwin_cached_devices; static const char *darwin_device_class = "IOUSBDevice"; +uint32_t libusb_testonly_fake_running_version __attribute__ ((visibility ("hidden"))); +int libusb_testonly_using_running_interface_version __attribute__ ((visibility ("hidden"))); +int libusb_testonly_using_running_device_version __attribute__ ((visibility ("hidden"))); +bool libusb_testonly_clear_running_version_cache __attribute__ ((visibility ("hidden"))); + #define DARWIN_CACHED_DEVICE(a) (((struct darwin_device_priv *)usbi_get_device_priv((a)))->dev) /* async event thread */ static pthread_t libusb_darwin_at; +/* protected by libusb_darwin_at_mutex */ +static bool libusb_darwin_at_started; + +static void darwin_exit(struct libusb_context *ctx); static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len); static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface); static int darwin_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface); @@ -96,6 +99,186 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io_service_t service, struct darwin_cached_device **cached_out, UInt64 *old_session_id); +struct darwin_iokit_interface { + uint32_t min_os_version; + uint32_t version; + CFUUIDRef interface_id; +}; + +static const struct darwin_iokit_interface *get_interface_interface(void) { + const struct darwin_iokit_interface interfaces[] = { +#if defined(kIOUSBInterfaceInterfaceID800) + { + .min_os_version = 101200, + .version = 800, + .interface_id = kIOUSBInterfaceInterfaceID800, + }, +#endif +#if defined(kIOUSBInterfaceInterfaceID700) + { + .min_os_version = 101000, + .version = 700, + .interface_id = kIOUSBInterfaceInterfaceID700, + }, +#endif +#if defined(kIOUSBInterfaceInterfaceID650) + { + .min_os_version = 100900, + .version = 650, + .interface_id = kIOUSBInterfaceInterfaceID650 + }, +#endif +#if defined(kIOUSBInterfaceInterfaceID550) + { + .min_os_version = 100803, + .version = 550, + .interface_id = kIOUSBInterfaceInterfaceID550, + }, +#endif +#if defined(kIOUSBInterfaceInterfaceID245) + { + .min_os_version = 100407, + .version = 245, + .interface_id = kIOUSBInterfaceInterfaceID245, + }, +#endif + { + .min_os_version = 100000, + .version = 220, + .interface_id = kIOUSBInterfaceInterfaceID220, + }, + { + .version = 0, + }, + }; + static struct darwin_iokit_interface cached_interface = {.version = 0}; + if (libusb_testonly_clear_running_version_cache) { + memset (&cached_interface, 0, sizeof (cached_interface)); + } + if (0 == cached_interface.version) { + uint32_t os_version = get_running_version(); + for (int i = 0 ; interfaces[i].version > 0 ; ++i) { + if (os_version >= interfaces[i].min_os_version && cached_interface.min_os_version < interfaces[i].min_os_version) { + cached_interface = interfaces[i]; + } + } + + libusb_testonly_using_running_interface_version = cached_interface.version; + } + + return &cached_interface; +} + +static CFUUIDRef get_interface_interface_id(void) { + return get_interface_interface()->interface_id; +} + +static int get_interface_interface_version(void) { + return get_interface_interface()->version; +} + +static const struct darwin_iokit_interface *get_device_interface(void) { + struct darwin_iokit_interface interfaces[] = { +#if defined(kIOUSBDeviceInterfaceID650) + { + .min_os_version = 100900, + .version = 650, + .interface_id = kIOUSBDeviceInterfaceID650, + }, +#endif +#if defined(kIOUSBDeviceInterfaceID500) + { + .min_os_version = 100703, + .version = 500, + .interface_id = kIOUSBDeviceInterfaceID500, + }, +#endif +#if defined(kIOUSBDeviceInterfaceID320) + { + .min_os_version = 100504, + .version = 320, + .interface_id = kIOUSBDeviceInterfaceID320, + }, +#endif +#if defined(kIOUSBDeviceInterfaceID300) + { + .min_os_version = 100500, + .version = 300, + .interface_id = kIOUSBDeviceInterfaceID300, + }, +#endif +#if defined(kIOUSBDeviceInterfaceID245) + { + .min_os_version = 100407, + .version = 245, + .interface_id = kIOUSBDeviceInterfaceID245, + }, +#endif + { + .min_os_version = 100000, + .version = 197, + .interface_id = kIOUSBDeviceInterfaceID197, + }, + { + .version = 0, + }, + }; + static struct darwin_iokit_interface cached_interface = {.version = 0}; + if (libusb_testonly_clear_running_version_cache) { + memset (&cached_interface, 0, sizeof (cached_interface)); + } + if (0 == cached_interface.version) { + uint32_t os_version = get_running_version(); + for (int i = 0 ; interfaces[i].version > 0 ; ++i) { + if (os_version >= interfaces[i].min_os_version && cached_interface.min_os_version < interfaces[i].min_os_version) { + cached_interface = interfaces[i]; + } + } + libusb_testonly_using_running_device_version = cached_interface.version; + } + + return &cached_interface; +} + +static CFUUIDRef get_device_interface_id(void) { + return get_device_interface()->interface_id; +} + +static int get_device_interface_version(void) { + return get_device_interface()->version; +} + +struct darwin_pipe_properties { + uint8_t number; + uint8_t direction; + uint8_t transfer_type; + uint16_t max_packet_size; + uint8_t interval; +}; +typedef struct darwin_pipe_properties darwin_pipe_properties_t; + +static IOReturn darwin_get_pipe_properties(struct darwin_interface *cInterface, uint8_t pipe, darwin_pipe_properties_t *out) { + IOReturn kresult; + +#if (MAX_INTERFACE_VERSION >= 550) + if (get_interface_interface_version() >= 550) { + IOUSBEndpointProperties pipe_properties = {.bVersion = kUSBEndpointPropertiesVersion3}; + kresult = (*IOINTERFACE_V(cInterface, 550))->GetPipePropertiesV3 (IOINTERFACE(cInterface), pipe, &pipe_properties); + if (kIOReturnSuccess == kresult) { + out->number = pipe_properties.bEndpointNumber; + out->direction = pipe_properties.bDirection; + out->transfer_type = pipe_properties.bTransferType; + out->max_packet_size = pipe_properties.wMaxPacketSize; + out->interval = pipe_properties.bInterval; + } + return kresult; + } +#endif + return (*IOINTERFACE(cInterface))->GetPipeProperties(IOINTERFACE(cInterface), pipe, &out->direction, + &out->number, &out->transfer_type, &out->max_packet_size, + &out->interval); +} + #if defined(ENABLE_LOGGING) static const char *darwin_error_str (IOReturn result) { static char string_buffer[50]; @@ -172,7 +355,72 @@ static enum libusb_error darwin_to_libusb (IOReturn result) { } } -/* this function must be called with the darwin_cached_devices_lock held */ +uint32_t get_running_version(void) { + if (libusb_testonly_fake_running_version > 0) { + return libusb_testonly_fake_running_version; + } + + int ret; +#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX == 1 + char os_version_string[64] = {'\0'};; + size_t os_version_string_len = sizeof(os_version_string) - 1; + + /* newer versions of macOS provide a sysctl for the OS version but this is not useful for iOS without + * code detecting this is iOS and a mapping from iOS -> macOS version. it is still useful to have since + * it provides the exact macOS version instead of the approximate version (as below). */ + ret = sysctlbyname("kern.osproductversion", os_version_string, &os_version_string_len, NULL, 0); + if (ret == 0) { + int major = 10, minor = 0, patch = 0; + ret = sscanf(os_version_string, "%i.%i.%i", &major, &minor, &patch); + if (ret < 2) { + usbi_err (NULL, "could not determine the running OS version, assuming 10.0, kern.osproductversion=%s", os_version_string); + return 100000; + } + return (major * 10000) + (minor * 100) + patch; + } +#endif + + char os_release_string[64] = {'\0'}; + size_t os_release_string_len = sizeof(os_release_string) - 1; + /* if the version can not be detected libusb assumes 10.0 so ignore any error here */ + ret = sysctlbyname("kern.osrelease", os_release_string, &os_release_string_len, NULL, 0); + if (ret != 0) { + usbi_err (NULL, "could not read kern.osrelease, errno=", errno); + return 100000; + } + + int darwin_major = 1, darwin_minor = 0; + ret = sscanf(os_release_string, "%i.%i", &darwin_major, &darwin_minor); + if (ret < 1) { + usbi_err (NULL, "could not determine the running Darwin version, assuming 1.3 (OS X 10.0), kern.osrelease=%s", os_release_string); + return 100000; + } + + int major = 10, minor = 0, patch = 0; + + if (1 == darwin_major && darwin_minor < 4) { + /* 10.0.x */ + } else if (darwin_major < 6) { + /* assume 10.1 for anything in this range */ + minor = 1; + } else if (darwin_major < 20) { + /* from macOS 10.2 through 10.15 the minor version can be calculated from the darwin_major by subtracting 4 and + * the patch level almost always matches darwin_minor. when the darwin_minor does not match the OS X patch level + * it is usually because Apple did not change it in a particular point release. when darwin_minor is changed it + * always matches the OS X/macOS patch level. */ + minor = darwin_major - 4; + patch = darwin_minor; + } else { + /* unlikely to be used as kern.osproductversion is available from 10.10 on */ + major = darwin_major - 9; + minor = darwin_minor; + /* ignore the patch level in this range */ + } + + return (major * 10000) + (minor * 100) + patch; +} + +/* this function must be called with the darwin_cached_devices_mutex held */ static void darwin_deref_cached_device(struct darwin_cached_device *cached_dev) { cached_dev->refcount--; /* free the device and remove it from the cache */ @@ -180,7 +428,7 @@ static void darwin_deref_cached_device(struct darwin_cached_device *cached_dev) list_del(&cached_dev->list); if (cached_dev->device) { - (*(cached_dev->device))->Release(cached_dev->device); + (*cached_dev->device)->Release(cached_dev->device); cached_dev->device = NULL; } IOObjectRelease (cached_dev->service); @@ -300,12 +548,12 @@ static bool get_ioregistry_value_data (io_service_t service, CFStringRef propert return success; } -static usb_device_t **darwin_device_from_service (struct libusb_context *ctx, io_service_t service) +static int darwin_device_from_service (struct libusb_context *ctx, io_service_t service, usb_device_t* device) { io_cf_plugin_ref_t *plugInInterface = NULL; - usb_device_t **device; IOReturn kresult; SInt32 score; + const int max_retries = 5; /* The IOCreatePlugInInterfaceForService function might consistently return @@ -325,17 +573,21 @@ static usb_device_t **darwin_device_from_service (struct libusb_context *ctx, io nanosleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 1000}, NULL); } - if (kIOReturnSuccess != kresult || !plugInInterface) { + if (kIOReturnSuccess != kresult) { usbi_dbg (ctx, "could not set up plugin for service: %s", darwin_error_str (kresult)); - return NULL; + return darwin_to_libusb(kresult); + } + if (!plugInInterface) { + usbi_dbg (ctx, "could not set up plugin for service"); + return LIBUSB_ERROR_OTHER; } - (void)(*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(DeviceInterfaceID), - (LPVOID)&device); + (void)(*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(get_device_interface_id()), + (LPVOID)device); /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */ (*plugInInterface)->Release (plugInInterface); - return device; + return LIBUSB_SUCCESS; } static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) { @@ -377,8 +629,6 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { struct darwin_cached_device *old_device; io_service_t device; - UInt64 session, locationID; - int ret; usbi_mutex_lock(&active_contexts_lock); @@ -386,7 +636,9 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { bool is_reenumerating = false; /* get the location from the i/o registry */ - ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session); + UInt64 session = 0; + bool ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session); + UInt32 locationID = 0; (void) get_ioregistry_value_number (device, CFSTR("locationID"), kCFNumberSInt32Type, &locationID); IOObjectRelease (device); if (!ret) @@ -394,18 +646,18 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { /* we need to match darwin_ref_cached_device call made in darwin_get_cached_device function otherwise no cached device will ever get freed */ - usbi_mutex_lock(&darwin_cached_devices_lock); + usbi_mutex_lock(&darwin_cached_devices_mutex); list_for_each_entry(old_device, &darwin_cached_devices, list, struct darwin_cached_device) { if (old_device->session == session) { if (old_device->in_reenumerate) { /* device is re-enumerating. do not dereference the device at this time. libusb_reset_device() * will deref if needed. */ - usbi_dbg (NULL, "detected device detached due to re-enumeration. sessionID: 0x%" PRIx64 ", locationID: 0x%" PRIx64, - session, locationID); + usbi_dbg (NULL, "detected device detached due to re-enumeration. sessionID: 0x%" PRIx64 + ", locationID: 0x%" PRIx32, session, locationID); /* the device object is no longer usable so go ahead and release it */ if (old_device->device) { - (*(old_device->device))->Release(old_device->device); + (*old_device->device)->Release(old_device->device); old_device->device = NULL; } @@ -418,7 +670,7 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { } } - usbi_mutex_unlock(&darwin_cached_devices_lock); + usbi_mutex_unlock(&darwin_cached_devices_mutex); if (is_reenumerating) { continue; } @@ -466,8 +718,8 @@ static void darwin_fail_startup(void) { } static void *darwin_event_thread_main (void *arg0) { + UNUSED(arg0); IOReturn kresult; - struct libusb_context *ctx = (struct libusb_context *)arg0; CFRunLoopRef runloop; CFRunLoopSourceRef libusb_shutdown_cfsource; CFRunLoopSourceContext libusb_shutdown_cfsourcectx; @@ -495,7 +747,7 @@ static void *darwin_event_thread_main (void *arg0) { io_iterator_t libusb_add_device_iterator; /* ctx must only be used for logging during thread startup */ - usbi_dbg (ctx, "creating hotplug event source"); + usbi_dbg (NULL, "creating hotplug event source"); runloop = CFRunLoopGetCurrent (); CFRetain (runloop); @@ -519,7 +771,7 @@ static void *darwin_event_thread_main (void *arg0) { NULL, &libusb_rem_device_iterator); if (kresult != kIOReturnSuccess) { - usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult)); + usbi_err (NULL, "could not add hotplug event source: %s", darwin_error_str (kresult)); CFRelease (libusb_shutdown_cfsource); CFRelease (runloop); darwin_fail_startup (); @@ -532,7 +784,7 @@ static void *darwin_event_thread_main (void *arg0) { NULL, &libusb_add_device_iterator); if (kresult != kIOReturnSuccess) { - usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult)); + usbi_err (NULL, "could not add hotplug event source: %s", darwin_error_str (kresult)); CFRelease (libusb_shutdown_cfsource); CFRelease (runloop); darwin_fail_startup (); @@ -542,7 +794,7 @@ static void *darwin_event_thread_main (void *arg0) { darwin_clear_iterator (libusb_rem_device_iterator); darwin_clear_iterator (libusb_add_device_iterator); - usbi_dbg (ctx, "darwin event thread ready to receive events"); + usbi_dbg (NULL, "darwin event thread ready to receive events"); /* signal the main thread that the hotplug runloop has been created. */ pthread_mutex_lock (&libusb_darwin_at_mutex); @@ -582,73 +834,82 @@ static void *darwin_event_thread_main (void *arg0) { pthread_exit (NULL); } -/* cleanup function to destroy cached devices */ +/* cleanup function to destroy cached devices. must be called with a lock on darwin_cached_devices_mutex */ static void darwin_cleanup_devices(void) { struct darwin_cached_device *dev, *next; list_for_each_entry_safe(dev, next, &darwin_cached_devices, list, struct darwin_cached_device) { + if (dev->refcount > 1) { + usbi_err(NULL, "device still referenced at libusb_exit"); + } darwin_deref_cached_device(dev); } } -static int darwin_init(struct libusb_context *ctx) { - bool first_init; - int rc; +/* must be called with a lock on darwin_cached_devices_mutex */ +static int darwin_first_time_init(void) { + if (NULL == darwin_cached_devices.next) { + list_init (&darwin_cached_devices); + } - first_init = (1 == ++init_count); + /* cache the interface versions that will be used. as a sanity check verify + * that the interface versions are non-zero. */ + const struct darwin_iokit_interface *interface_interface = get_interface_interface(); + const struct darwin_iokit_interface *device_interface = get_device_interface(); + if (0 == interface_interface->version || 0 == device_interface->version) { + usbi_err(NULL, "could not determine the device or interface interface to use with this version " + "of macOS (or MacOS X), current_running_version = %" PRIu32, get_running_version()); + return LIBUSB_ERROR_OTHER; + } - do { - if (first_init) { - if (NULL == darwin_cached_devices.next) { - list_init (&darwin_cached_devices); - } - assert(list_empty(&darwin_cached_devices)); -#if !defined(HAVE_CLOCK_GETTIME) - /* create the clocks that will be used if clock_gettime() is not available */ - host_name_port_t host_self; - - host_self = mach_host_self(); - host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime); - host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic); - mach_port_deallocate(mach_task_self(), host_self); -#endif - } + if (!list_empty(&darwin_cached_devices)) { + usbi_err(NULL, "libusb_device reference not released on last exit. will not continue"); + return LIBUSB_ERROR_OTHER; + } - rc = darwin_scan_devices (ctx); - if (LIBUSB_SUCCESS != rc) - break; + int rc = pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, NULL); + if (0 != rc) { + usbi_err (NULL, "could not create event thread, error %d", rc); + return LIBUSB_ERROR_OTHER; + } - if (first_init) { - rc = pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx); - if (0 != rc) { - usbi_err (ctx, "could not create event thread, error %d", rc); - rc = LIBUSB_ERROR_OTHER; - break; - } + pthread_mutex_lock (&libusb_darwin_at_mutex); + libusb_darwin_at_started = true; + while (NULL == libusb_darwin_acfl) { + pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex); + } - pthread_mutex_lock (&libusb_darwin_at_mutex); - while (!libusb_darwin_acfl) - pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex); - if (libusb_darwin_acfl == LIBUSB_DARWIN_STARTUP_FAILURE) { - libusb_darwin_acfl = NULL; - rc = LIBUSB_ERROR_OTHER; - } - pthread_mutex_unlock (&libusb_darwin_at_mutex); + if (libusb_darwin_acfl == LIBUSB_DARWIN_STARTUP_FAILURE) { + libusb_darwin_acfl = NULL; + rc = LIBUSB_ERROR_OTHER; + } + pthread_mutex_unlock (&libusb_darwin_at_mutex); - if (0 != rc) - pthread_join (libusb_darwin_at, NULL); + return rc; +} + +static int darwin_init_context(struct libusb_context *ctx) { + usbi_mutex_lock(&darwin_cached_devices_mutex); + + bool first_init = (1 == ++init_count); + + if (first_init) { + int rc = darwin_first_time_init(); + if (LIBUSB_SUCCESS != rc) { + usbi_mutex_unlock(&darwin_cached_devices_mutex); + return rc; } - } while (0); + } + usbi_mutex_unlock(&darwin_cached_devices_mutex); + + return darwin_scan_devices (ctx); +} +static int darwin_init(struct libusb_context *ctx) { + int rc = darwin_init_context(ctx); if (LIBUSB_SUCCESS != rc) { - if (first_init) { - darwin_cleanup_devices (); -#if !defined(HAVE_CLOCK_GETTIME) - mach_port_deallocate(mach_task_self(), clock_realtime); - mach_port_deallocate(mach_task_self(), clock_monotonic); -#endif - } - --init_count; + /* clean up any allocated resources */ + darwin_exit(ctx); } return rc; @@ -657,23 +918,26 @@ static int darwin_init(struct libusb_context *ctx) { static void darwin_exit (struct libusb_context *ctx) { UNUSED(ctx); + usbi_mutex_lock(&darwin_cached_devices_mutex); if (0 == --init_count) { /* stop the event runloop and wait for the thread to terminate. */ pthread_mutex_lock (&libusb_darwin_at_mutex); - CFRunLoopSourceSignal (libusb_darwin_acfls); - CFRunLoopWakeUp (libusb_darwin_acfl); - while (libusb_darwin_acfl) - pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex); + if (NULL != libusb_darwin_acfls) { + CFRunLoopSourceSignal (libusb_darwin_acfls); + CFRunLoopWakeUp (libusb_darwin_acfl); + while (libusb_darwin_acfl) + pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex); + } + + if (libusb_darwin_at_started) { + pthread_join (libusb_darwin_at, NULL); + libusb_darwin_at_started = false; + } pthread_mutex_unlock (&libusb_darwin_at_mutex); - pthread_join (libusb_darwin_at, NULL); darwin_cleanup_devices (); - -#if !defined(HAVE_CLOCK_GETTIME) - mach_port_deallocate(mach_task_self(), clock_realtime); - mach_port_deallocate(mach_task_self(), clock_monotonic); -#endif } + usbi_mutex_unlock(&darwin_cached_devices_mutex); } static int get_configuration_index (struct libusb_device *dev, UInt8 config_value) { @@ -683,12 +947,12 @@ static int get_configuration_index (struct libusb_device *dev, UInt8 config_valu IOReturn kresult; /* is there a simpler way to determine the index? */ - kresult = (*(priv->device))->GetNumberOfConfigurations (priv->device, &numConfig); + kresult = (*priv->device)->GetNumberOfConfigurations (priv->device, &numConfig); if (kresult != kIOReturnSuccess) return darwin_to_libusb (kresult); for (i = 0 ; i < numConfig ; i++) { - (*(priv->device))->GetConfigurationDescriptorPtr (priv->device, i, &desc); + (*priv->device)->GetConfigurationDescriptorPtr (priv->device, i, &desc); if (desc->bConfigurationValue == config_value) return i; @@ -740,7 +1004,7 @@ static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t confi /* check whether the os has configured the device */ static enum libusb_error darwin_check_configuration (struct libusb_context *ctx, struct darwin_cached_device *dev) { - usb_device_t **darwin_device = dev->device; + usb_device_t darwin_device = dev->device; IOUSBConfigurationDescriptorPtr configDesc; IOUSBFindInterfaceRequest request; @@ -776,7 +1040,7 @@ static enum libusb_error darwin_check_configuration (struct libusb_context *ctx, request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; request.bAlternateSetting = kIOUSBFindInterfaceDontCare; - kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator); + kresult = (*darwin_device)->CreateInterfaceIterator(darwin_device, &request, &interface_iterator); if (kresult != kIOReturnSuccess) return darwin_to_libusb (kresult); @@ -805,7 +1069,7 @@ static enum libusb_error darwin_check_configuration (struct libusb_context *ctx, return LIBUSB_SUCCESS; } -static IOReturn darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) { +static IOReturn darwin_request_descriptor (usb_device_t device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) { IOUSBDevRequestTO req; assert(buffer_size <= UINT16_MAX); @@ -826,7 +1090,7 @@ static IOReturn darwin_request_descriptor (usb_device_t **device, UInt8 desc, UI } static enum libusb_error darwin_cache_device_descriptor (struct libusb_context *ctx, struct darwin_cached_device *dev) { - usb_device_t **device = dev->device; + usb_device_t device = dev->device; int retries = 1; long delay = 30000; // microseconds int unsuspended = 0, try_unsuspend = 1, try_reconfigure = 1; @@ -877,15 +1141,17 @@ static enum libusb_error darwin_cache_device_descriptor (struct libusb_context * if (kIOReturnSuccess != ret && is_open && try_unsuspend) { /* device may be suspended. unsuspend it and try again */ -#if DeviceVersion >= 320 - UInt32 info = 0; +#if MAX_DEVICE_VERSION >= 320 + if (get_device_interface_version() >= 320) { + UInt32 info = 0; - /* IOUSBFamily 320+ provides a way to detect device suspension but earlier versions do not */ - (void)(*device)->GetUSBDeviceInformation (device, &info); + /* IOUSBFamily 320+ provides a way to detect device suspension but earlier versions do not */ + (void)(*IODEVICE_V(device, 320))->GetUSBDeviceInformation (device, &info); - /* note that the device was suspended */ - if (info & (1U << kUSBInformationDeviceIsSuspendedBit) || 0 == info) - try_unsuspend = 1; + /* note that the device was suspended */ + if (info & (1U << kUSBInformationDeviceIsSuspendedBit) || 0 == info) + try_unsuspend = 1; + } #endif if (try_unsuspend) { @@ -998,7 +1264,7 @@ static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io UInt64 sessionID = 0, parent_sessionID = 0; UInt32 locationID = 0; enum libusb_error ret = LIBUSB_SUCCESS; - usb_device_t **device; + usb_device_t device; UInt8 port = 0; /* assuming sessionID != 0 normally (never seen it be 0) */ @@ -1018,10 +1284,10 @@ static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io usbi_dbg(ctx, "parent sessionID: 0x%" PRIx64, parent_sessionID); } - usbi_mutex_lock(&darwin_cached_devices_lock); + usbi_mutex_lock(&darwin_cached_devices_mutex); do { list_for_each_entry(new_device, &darwin_cached_devices, list, struct darwin_cached_device) { - usbi_dbg(ctx, "matching sessionID/locationID 0x%" PRIx64 "/0x%x against cached device with sessionID/locationID 0x%" PRIx64 "/0x%x", + usbi_dbg(ctx, "matching sessionID/locationID 0x%" PRIx64 "/0x%" PRIx32 " against cached device with sessionID/locationID 0x%" PRIx64 "/0x%" PRIx32, sessionID, locationID, new_device->session, new_device->location); if (new_device->location == locationID && new_device->in_reenumerate) { usbi_dbg (ctx, "found cached device with matching location that is being re-enumerated"); @@ -1041,9 +1307,8 @@ static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io usbi_dbg(ctx, "caching new device with sessionID 0x%" PRIx64, sessionID); - device = darwin_device_from_service (ctx, service); - if (!device) { - ret = LIBUSB_ERROR_NO_DEVICE; + ret = darwin_device_from_service (ctx, service, &device); + if (LIBUSB_SUCCESS != ret) { break; } @@ -1094,7 +1359,7 @@ static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io } } while (0); - usbi_mutex_unlock(&darwin_cached_devices_lock); + usbi_mutex_unlock(&darwin_cached_devices_mutex); return ret; } @@ -1158,7 +1423,7 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct dev->parent_dev = usbi_get_device_by_session_id (ctx, (unsigned long) cached_device->parent_session); } - (*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed); + (*priv->dev->device)->GetDeviceSpeed (priv->dev->device, &devSpeed); switch (devSpeed) { case kUSBDeviceSpeedLow: dev->speed = LIBUSB_SPEED_LOW; break; @@ -1227,7 +1492,7 @@ static int darwin_open (struct libusb_device_handle *dev_handle) { if (0 == dpriv->open_count) { /* try to open the device */ - kresult = (*(dpriv->device))->USBDeviceOpenSeize (dpriv->device); + kresult = (*dpriv->device)->USBDeviceOpenSeize (dpriv->device); if (kresult != kIOReturnSuccess) { usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceOpen: %s", darwin_error_str(kresult)); @@ -1242,12 +1507,13 @@ static int darwin_open (struct libusb_device_handle *dev_handle) { } /* create async event source */ - kresult = (*(dpriv->device))->CreateDeviceAsyncEventSource (dpriv->device, &priv->cfSource); + kresult = (*dpriv->device)->CreateDeviceAsyncEventSource (dpriv->device, + &priv->cfSource); if (kresult != kIOReturnSuccess) { usbi_err (HANDLE_CTX (dev_handle), "CreateDeviceAsyncEventSource: %s", darwin_error_str(kresult)); if (priv->is_open) { - (*(dpriv->device))->USBDeviceClose (dpriv->device); + (*dpriv->device)->USBDeviceClose (dpriv->device); } priv->is_open = false; @@ -1303,7 +1569,7 @@ static void darwin_close (struct libusb_device_handle *dev_handle) { if (priv->is_open) { /* close the device */ - kresult = (*(dpriv->device))->USBDeviceClose(dpriv->device); + kresult = (*dpriv->device)->USBDeviceClose(dpriv->device); if (kresult != kIOReturnSuccess) { /* Log the fact that we had a problem closing the file, however failing a * close isn't really an error, so return success anyway */ @@ -1335,7 +1601,7 @@ static enum libusb_error darwin_set_configuration(struct libusb_device_handle *d if (dev_handle->claimed_interfaces & (1U << i)) darwin_release_interface (dev_handle, i); - kresult = (*(dpriv->device))->SetConfiguration (dpriv->device, (UInt8)config); + kresult = (*dpriv->device)->SetConfiguration (dpriv->device, (UInt8)config); if (kresult != kIOReturnSuccess) return darwin_to_libusb (kresult); @@ -1349,7 +1615,7 @@ static enum libusb_error darwin_set_configuration(struct libusb_device_handle *d return LIBUSB_SUCCESS; } -static IOReturn darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_service_t *usbInterfacep) { +static IOReturn darwin_get_interface (usb_device_t darwin_device, uint8_t ifc, io_service_t *usbInterfacep) { IOUSBFindInterfaceRequest request; IOReturn kresult; io_iterator_t interface_iterator; @@ -1364,7 +1630,7 @@ static IOReturn darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; request.bAlternateSetting = kIOUSBFindInterfaceDontCare; - kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator); + kresult = (*darwin_device)->CreateInterfaceIterator(darwin_device, &request, &interface_iterator); if (kresult != kIOReturnSuccess) return kresult; @@ -1386,29 +1652,34 @@ static IOReturn darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, return kIOReturnSuccess; } +static const struct libusb_interface_descriptor *get_interface_descriptor_by_number(struct libusb_device_handle *dev_handle, struct libusb_config_descriptor *conf_desc, int iface, uint8_t altsetting) +{ + int i; + + for (i = 0; i < conf_desc->bNumInterfaces; i++) { + if (altsetting < conf_desc->interface[i].num_altsetting && conf_desc->interface[i].altsetting[altsetting].bInterfaceNumber == iface) { + return &conf_desc->interface[i].altsetting[altsetting]; + } + } + + usbi_err(HANDLE_CTX(dev_handle), "interface %d with altsetting %d not found for device", iface, (int)altsetting); + return NULL; +} + static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle, uint8_t iface) { struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); /* current interface */ struct darwin_interface *cInterface = &priv->interfaces[iface]; -#if InterfaceVersion >= 550 - IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3}; -#else - UInt8 dont_care1, dont_care3; - UInt16 dont_care2; -#endif - IOReturn kresult; - - UInt8 numep, direction, number; + uint8_t numep; int rc; struct libusb_context *ctx = HANDLE_CTX (dev_handle); - usbi_dbg (ctx, "building table of endpoints."); /* retrieve the total number of endpoints on this interface */ - kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep); + kresult = (*IOINTERFACE(cInterface))->GetNumEndpoints(IOINTERFACE(cInterface), &numep); if (kresult != kIOReturnSuccess) { usbi_err (ctx, "can't get number of endpoints for interface: %s", darwin_error_str(kresult)); return darwin_to_libusb (kresult); @@ -1416,21 +1687,16 @@ static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle, /* iterate through pipe references */ for (UInt8 i = 1 ; i <= numep ; i++) { -#if InterfaceVersion >= 550 - kresult = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, i, &pipeProperties); - number = pipeProperties.bEndpointNumber; - direction = pipeProperties.bDirection; -#else - kresult = (*(cInterface->interface))->GetPipeProperties(cInterface->interface, i, &direction, &number, &dont_care1, - &dont_care2, &dont_care3); -#endif + darwin_pipe_properties_t pipe_properties; + kresult = darwin_get_pipe_properties(cInterface, i, &pipe_properties); if (kresult != kIOReturnSuccess) { /* probably a buggy device. try to get the endpoint address from the descriptors */ struct libusb_config_descriptor *config; + const struct libusb_interface_descriptor *if_desc; const struct libusb_endpoint_descriptor *endpoint_desc; UInt8 alt_setting; - kresult = (*(cInterface->interface))->GetAlternateSetting (cInterface->interface, &alt_setting); + kresult = (*IOINTERFACE(cInterface))->GetAlternateSetting (IOINTERFACE(cInterface), &alt_setting); if (kresult != kIOReturnSuccess) { usbi_err (HANDLE_CTX (dev_handle), "can't get alternate setting for interface"); return darwin_to_libusb (kresult); @@ -1441,15 +1707,19 @@ static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle, return rc; } - if (iface >= config->bNumInterfaces) { - usbi_err (HANDLE_CTX (dev_handle), "interface %d out of range for device", iface); + if_desc = get_interface_descriptor_by_number (dev_handle, config, iface, alt_setting); + if (if_desc == NULL) { + libusb_free_config_descriptor (config); return LIBUSB_ERROR_NOT_FOUND; } - endpoint_desc = config->interface[iface].altsetting[alt_setting].endpoint + i - 1; + + endpoint_desc = if_desc->endpoint + i - 1; cInterface->endpoint_addrs[i - 1] = endpoint_desc->bEndpointAddress; + libusb_free_config_descriptor (config); } else { - cInterface->endpoint_addrs[i - 1] = (UInt8)(((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK)); + cInterface->endpoint_addrs[i - 1] = (UInt8)(((kUSBIn == pipe_properties.direction) << kUSBRqDirnShift) | + (pipe_properties.number & LIBUSB_ENDPOINT_ADDRESS_MASK)); } usbi_dbg (ctx, "interface: %i pipe %i: dir: %i number: %i", iface, i, cInterface->endpoint_addrs[i - 1] >> kUSBRqDirnShift, @@ -1521,18 +1791,22 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8 /* Do the actual claim */ kresult = (*plugInInterface)->QueryInterface(plugInInterface, - CFUUIDGetUUIDBytes(InterfaceInterfaceID), - (LPVOID)&cInterface->interface); + CFUUIDGetUUIDBytes(get_interface_interface_id()), + (LPVOID)&IOINTERFACE(cInterface)); /* We no longer need the intermediate plug-in */ /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */ (*plugInInterface)->Release (plugInInterface); - if (kresult != kIOReturnSuccess || !cInterface->interface) { + if (kresult != kIOReturnSuccess) { usbi_err (ctx, "QueryInterface: %s", darwin_error_str(kresult)); return darwin_to_libusb (kresult); } + if (!IOINTERFACE(cInterface)) { + usbi_err (ctx, "QueryInterface: returned null interface"); + return LIBUSB_ERROR_OTHER; + } /* claim the interface */ - kresult = (*(cInterface->interface))->USBInterfaceOpen(cInterface->interface); + kresult = (*IOINTERFACE(cInterface))->USBInterfaceOpen(IOINTERFACE(cInterface)); if (kresult != kIOReturnSuccess) { usbi_info (ctx, "USBInterfaceOpen: %s", darwin_error_str(kresult)); return darwin_to_libusb (kresult); @@ -1550,7 +1824,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8 cInterface->cfSource = NULL; /* create async event source */ - kresult = (*(cInterface->interface))->CreateInterfaceAsyncEventSource (cInterface->interface, &cInterface->cfSource); + kresult = (*IOINTERFACE(cInterface))->CreateInterfaceAsyncEventSource (IOINTERFACE(cInterface), &cInterface->cfSource); if (kresult != kIOReturnSuccess) { usbi_err (ctx, "could not create async event source"); @@ -1576,8 +1850,9 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, uin struct darwin_interface *cInterface = &priv->interfaces[iface]; /* Check to see if an interface is open */ - if (!cInterface->interface) + if (!IOINTERFACE(cInterface)) { return LIBUSB_SUCCESS; + } /* clean up endpoint data */ cInterface->num_endpoints = 0; @@ -1589,15 +1864,15 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, uin cInterface->cfSource = NULL; } - kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface); + kresult = (*IOINTERFACE(cInterface))->USBInterfaceClose(IOINTERFACE(cInterface)); if (kresult != kIOReturnSuccess) usbi_warn (HANDLE_CTX (dev_handle), "USBInterfaceClose: %s", darwin_error_str(kresult)); - kresult = (*(cInterface->interface))->Release(cInterface->interface); + kresult = (*IOINTERFACE(cInterface))->Release(IOINTERFACE(cInterface)); if (kresult != kIOReturnSuccess) usbi_warn (HANDLE_CTX (dev_handle), "Release: %s", darwin_error_str(kresult)); - cInterface->interface = (usb_interface_t **) IO_OBJECT_NULL; + IOINTERFACE(cInterface) = NULL; return darwin_to_libusb (kresult); } @@ -1607,7 +1882,7 @@ static int check_alt_setting_and_clear_halt(struct libusb_device_handle *dev_han IOReturn kresult; uint8_t current_alt_setting; - kresult = (*(cInterface->interface))->GetAlternateSetting (cInterface->interface, ¤t_alt_setting); + kresult = (*IOINTERFACE(cInterface))->GetAlternateSetting (IOINTERFACE(cInterface), ¤t_alt_setting); if (kresult == kIOReturnSuccess && altsetting != current_alt_setting) { return LIBUSB_ERROR_PIPE; } @@ -1634,10 +1909,11 @@ static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_hand /* current interface */ struct darwin_interface *cInterface = &priv->interfaces[iface]; - if (!cInterface->interface) + if (!IOINTERFACE(cInterface)) { return LIBUSB_ERROR_NO_DEVICE; + } - kresult = (*(cInterface->interface))->SetAlternateInterface (cInterface->interface, altsetting); + kresult = (*IOINTERFACE(cInterface))->SetAlternateInterface (IOINTERFACE(cInterface), altsetting); if (kresult == kIOReturnSuccess) { /* update the list of endpoints */ ret = get_endpoints (dev_handle, iface); @@ -1689,7 +1965,7 @@ static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned c } /* newer versions of darwin support clearing additional bits on the device's endpoint */ - kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef); + kresult = (*IOINTERFACE(cInterface))->ClearPipeStallBothEnds(IOINTERFACE(cInterface), pipeRef); if (kresult != kIOReturnSuccess) usbi_warn (HANDLE_CTX (dev_handle), "ClearPipeStall: %s", darwin_error_str (kresult)); @@ -1783,12 +2059,12 @@ static int darwin_reenumerate_device (struct libusb_device_handle *dev_handle, b cached_configurations = alloca (sizeof (*cached_configurations) * descriptor.bNumConfigurations); for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) { - (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration); + (*dpriv->device)->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration); memcpy (cached_configurations + i, cached_configuration, sizeof (cached_configurations[i])); } /* if we need to release capture */ - if (HAS_CAPTURE_DEVICE()) { + if (get_running_version() >= 101000) { if (capture) { #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 options |= kUSBReEnumerateCaptureDeviceMask; @@ -1799,7 +2075,7 @@ static int darwin_reenumerate_device (struct libusb_device_handle *dev_handle, b } /* from macOS 10.11 ResetDevice no longer does anything so just use USBDeviceReEnumerate */ - kresult = (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, options); + kresult = (*dpriv->device)->USBDeviceReEnumerate (dpriv->device, options); if (kresult != kIOReturnSuccess) { usbi_err (ctx, "USBDeviceReEnumerate: %s", darwin_error_str (kresult)); dpriv->in_reenumerate = false; @@ -1844,7 +2120,7 @@ static int darwin_reenumerate_device (struct libusb_device_handle *dev_handle, b } for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) { - (void) (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration); + (void) (*dpriv->device)->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration); if (memcmp (cached_configuration, cached_configurations + i, sizeof (cached_configurations[i]))) { usbi_dbg (ctx, "darwin/reenumerate_device: configuration descriptor %d changed", i); return LIBUSB_ERROR_NOT_FOUND; @@ -1864,7 +2140,7 @@ static int darwin_reset_device (struct libusb_device_handle *dev_handle) { #if !defined(TARGET_OS_OSX) || TARGET_OS_OSX == 1 if (dpriv->capture_count > 0) { /* we have to use ResetDevice as USBDeviceReEnumerate() loses the authorization for capture */ - kresult = (*(dpriv->device))->ResetDevice (dpriv->device); + kresult = (*dpriv->device)->ResetDevice (dpriv->device); ret = darwin_to_libusb (kresult); } else { ret = darwin_reenumerate_device (dev_handle, false); @@ -1945,10 +2221,10 @@ static void darwin_destroy_device(struct libusb_device *dev) { if (dpriv->dev) { /* need to hold the lock in case this is the last reference to the device */ - usbi_mutex_lock(&darwin_cached_devices_lock); + usbi_mutex_lock(&darwin_cached_devices_mutex); darwin_deref_cached_device (dpriv->dev); dpriv->dev = NULL; - usbi_mutex_unlock(&darwin_cached_devices_lock); + usbi_mutex_unlock(&darwin_cached_devices_mutex); } } @@ -1956,17 +2232,10 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); IOReturn ret; - uint8_t transferType; uint8_t pipeRef; - uint16_t maxPacketSize; struct darwin_interface *cInterface; -#if InterfaceVersion >= 550 - IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3}; -#else - /* None of the values below are used in libusb for bulk transfers */ - uint8_t direction, number, interval; -#endif + darwin_pipe_properties_t pipe_properties; if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) { usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); @@ -1974,47 +2243,38 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) { return LIBUSB_ERROR_NOT_FOUND; } -#if InterfaceVersion >= 550 - ret = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, pipeRef, &pipeProperties); - - transferType = pipeProperties.bTransferType; - maxPacketSize = pipeProperties.wMaxPacketSize; -#else - ret = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number, - &transferType, &maxPacketSize, &interval); -#endif - - if (ret) { + ret = darwin_get_pipe_properties(cInterface, pipeRef, &pipe_properties); + if (kIOReturnSuccess != ret) { usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out", darwin_error_str(ret), ret); return darwin_to_libusb (ret); } - if (0 != (transfer->length % maxPacketSize)) { + if (0 != (transfer->length % pipe_properties.max_packet_size)) { /* do not need a zero packet */ transfer->flags &= ~LIBUSB_TRANSFER_ADD_ZERO_PACKET; } /* submit the request */ /* timeouts are unavailable on interrupt endpoints */ - if (transferType == kUSBInterrupt) { + if (pipe_properties.transfer_type == kUSBInterrupt) { if (IS_XFERIN(transfer)) - ret = (*(cInterface->interface))->ReadPipeAsync(cInterface->interface, pipeRef, transfer->buffer, - (UInt32)transfer->length, darwin_async_io_callback, itransfer); + ret = (*IOINTERFACE(cInterface))->ReadPipeAsync(IOINTERFACE(cInterface), pipeRef, transfer->buffer, + (UInt32)transfer->length, darwin_async_io_callback, itransfer); else - ret = (*(cInterface->interface))->WritePipeAsync(cInterface->interface, pipeRef, transfer->buffer, - (UInt32)transfer->length, darwin_async_io_callback, itransfer); + ret = (*IOINTERFACE(cInterface))->WritePipeAsync(IOINTERFACE(cInterface), pipeRef, transfer->buffer, + (UInt32)transfer->length, darwin_async_io_callback, itransfer); } else { itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; if (IS_XFERIN(transfer)) - ret = (*(cInterface->interface))->ReadPipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer, - (UInt32)transfer->length, transfer->timeout, transfer->timeout, - darwin_async_io_callback, itransfer); + ret = (*IOINTERFACE(cInterface))->ReadPipeAsyncTO(IOINTERFACE(cInterface), pipeRef, transfer->buffer, + (UInt32)transfer->length, transfer->timeout, transfer->timeout, + darwin_async_io_callback, itransfer); else - ret = (*(cInterface->interface))->WritePipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer, - (UInt32)transfer->length, transfer->timeout, transfer->timeout, - darwin_async_io_callback, itransfer); + ret = (*IOINTERFACE(cInterface))->WritePipeAsyncTO(IOINTERFACE(cInterface), pipeRef, transfer->buffer, + (UInt32)transfer->length, transfer->timeout, transfer->timeout, + darwin_async_io_callback, itransfer); } if (ret) @@ -2024,7 +2284,7 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) { return darwin_to_libusb (ret); } -#if InterfaceVersion >= 550 +#if MAX_INTERFACE_VERSION >= 550 static int submit_stream_transfer(struct usbi_transfer *itransfer) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); struct darwin_interface *cInterface; @@ -2037,16 +2297,22 @@ static int submit_stream_transfer(struct usbi_transfer *itransfer) { return LIBUSB_ERROR_NOT_FOUND; } + if (get_interface_interface_version() < 550) { + usbi_err (TRANSFER_CTX(transfer), "IOUSBFamily version %d does not support bulk stream transfers", + get_interface_interface_version()); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; if (IS_XFERIN(transfer)) - ret = (*(cInterface->interface))->ReadStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id, - transfer->buffer, (UInt32)transfer->length, transfer->timeout, - transfer->timeout, darwin_async_io_callback, itransfer); + ret = (*IOINTERFACE_V(cInterface, 550))->ReadStreamsPipeAsyncTO(IOINTERFACE(cInterface), pipeRef, itransfer->stream_id, + transfer->buffer, (UInt32)transfer->length, transfer->timeout, + transfer->timeout, darwin_async_io_callback, itransfer); else - ret = (*(cInterface->interface))->WriteStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id, - transfer->buffer, (UInt32)transfer->length, transfer->timeout, - transfer->timeout, darwin_async_io_callback, itransfer); + ret = (*IOINTERFACE_V(cInterface, 550))->WriteStreamsPipeAsyncTO(IOINTERFACE(cInterface), pipeRef, itransfer->stream_id, + transfer->buffer, (UInt32)transfer->length, transfer->timeout, + transfer->timeout, darwin_async_io_callback, itransfer); if (ret) usbi_err (TRANSFER_CTX (transfer), "bulk stream transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out", @@ -2061,18 +2327,11 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); IOReturn kresult; - uint8_t pipeRef, interval; + uint8_t pipeRef; UInt64 frame; AbsoluteTime atTime; int i; -#if InterfaceVersion >= 550 - IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3}; -#else - /* None of the values below are used in libusb for iso transfers */ - uint8_t direction, number, transferType; - uint16_t maxPacketSize; -#endif - + darwin_pipe_properties_t pipe_properties; struct darwin_interface *cInterface; /* construct an array of IOUSBIsocFrames, reuse the old one if the sizes are the same */ @@ -2103,13 +2362,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { } /* determine the properties of this endpoint and the speed of the device */ -#if InterfaceVersion >= 550 - kresult = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, pipeRef, &pipeProperties); - interval = pipeProperties.bInterval; -#else - kresult = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number, - &transferType, &maxPacketSize, &interval); -#endif + kresult = darwin_get_pipe_properties(cInterface, pipeRef, &pipe_properties); if (kresult != kIOReturnSuccess) { usbi_err (TRANSFER_CTX (transfer), "failed to get pipe properties: %d", kresult); free(tpriv->isoc_framelist); @@ -2119,7 +2372,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { } /* Last but not least we need the bus frame number */ - kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime); + kresult = (*IOINTERFACE(cInterface))->GetBusFrameNumber(IOINTERFACE(cInterface), &frame, &atTime); if (kresult != kIOReturnSuccess) { usbi_err (TRANSFER_CTX (transfer), "failed to get bus frame number: %d", kresult); free(tpriv->isoc_framelist); @@ -2136,20 +2389,20 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { /* submit the request */ if (IS_XFERIN(transfer)) - kresult = (*(cInterface->interface))->ReadIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame, - (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, - itransfer); + kresult = (*IOINTERFACE(cInterface))->ReadIsochPipeAsync(IOINTERFACE(cInterface), pipeRef, transfer->buffer, frame, + (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, + itransfer); else - kresult = (*(cInterface->interface))->WriteIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame, - (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, - itransfer); + kresult = (*IOINTERFACE(cInterface))->WriteIsochPipeAsync(IOINTERFACE(cInterface), pipeRef, transfer->buffer, frame, + (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, + itransfer); if (LIBUSB_SPEED_FULL == transfer->dev_handle->dev->speed) /* Full speed */ - cInterface->frames[transfer->endpoint] = frame + (UInt32)transfer->num_iso_packets * (1U << (interval - 1)); + cInterface->frames[transfer->endpoint] = frame + (UInt32)transfer->num_iso_packets * (1U << (pipe_properties.interval - 1)); else /* High/super speed */ - cInterface->frames[transfer->endpoint] = frame + (UInt32)transfer->num_iso_packets * (1U << (interval - 1)) / 8; + cInterface->frames[transfer->endpoint] = frame + (UInt32)transfer->num_iso_packets * (1U << (pipe_properties.interval - 1)) / 8; if (kresult != kIOReturnSuccess) { usbi_err (TRANSFER_CTX (transfer), "isochronous transfer failed (dir: %s): %s", IS_XFERIN(transfer) ? "In" : "Out", @@ -2197,10 +2450,11 @@ static int submit_control_transfer(struct usbi_transfer *itransfer) { return LIBUSB_ERROR_NOT_FOUND; } - kresult = (*(cInterface->interface))->ControlRequestAsyncTO (cInterface->interface, pipeRef, &(tpriv->req), darwin_async_io_callback, itransfer); + kresult = (*IOINTERFACE(cInterface))->ControlRequestAsyncTO (IOINTERFACE(cInterface), pipeRef, + &(tpriv->req), darwin_async_io_callback, itransfer); } else /* control request on endpoint 0 */ - kresult = (*(dpriv->device))->DeviceRequestAsyncTO(dpriv->device, &(tpriv->req), darwin_async_io_callback, itransfer); + kresult = (*dpriv->device)->DeviceRequestAsyncTO(dpriv->device, &(tpriv->req), darwin_async_io_callback, itransfer); if (kresult != kIOReturnSuccess) usbi_err (TRANSFER_CTX (transfer), "control request failed: %s", darwin_error_str(kresult)); @@ -2220,7 +2474,7 @@ static int darwin_submit_transfer(struct usbi_transfer *itransfer) { case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: return submit_iso_transfer(itransfer); case LIBUSB_TRANSFER_TYPE_BULK_STREAM: -#if InterfaceVersion >= 550 +#if MAX_INTERFACE_VERSION >= 550 return submit_stream_transfer(itransfer); #else usbi_err (TRANSFER_CTX(transfer), "IOUSBFamily version does not support bulk stream transfers"); @@ -2239,10 +2493,11 @@ static int cancel_control_transfer(struct usbi_transfer *itransfer) { usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions control pipe"); - if (!dpriv->device) + if (!dpriv->device) { return LIBUSB_ERROR_NO_DEVICE; + } - kresult = (*(dpriv->device))->USBDeviceAbortPipeZero (dpriv->device); + kresult = (*dpriv->device)->USBDeviceAbortPipeZero (dpriv->device); return darwin_to_libusb (kresult); } @@ -2262,23 +2517,29 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) { return LIBUSB_ERROR_NOT_FOUND; } - if (!dpriv->device) + if (!dpriv->device) { return LIBUSB_ERROR_NO_DEVICE; + } usbi_warn (ctx, "aborting all transactions on interface %d pipe %d", iface, pipeRef); /* abort transactions */ -#if InterfaceVersion >= 550 - if (LIBUSB_TRANSFER_TYPE_BULK_STREAM == transfer->type) - (*(cInterface->interface))->AbortStreamsPipe (cInterface->interface, pipeRef, itransfer->stream_id); - else +#if MAX_INTERFACE_VERSION >= 550 + if (LIBUSB_TRANSFER_TYPE_BULK_STREAM == transfer->type && get_interface_interface_version() >= 550) { + kresult = (*IOINTERFACE_V(cInterface, 550))->AbortStreamsPipe (IOINTERFACE(cInterface), pipeRef, itransfer->stream_id); + } else #endif - (*(cInterface->interface))->AbortPipe (cInterface->interface, pipeRef); + { + kresult = (*IOINTERFACE(cInterface))->AbortPipe (IOINTERFACE(cInterface), pipeRef); + } - usbi_dbg (ctx, "calling clear pipe stall to clear the data toggle bit"); - /* newer versions of darwin support clearing additional bits on the device's endpoint */ - kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef); + if (get_interface_interface_version() <= 245) { + /* with older releases of IOUSBFamily the OS always clears the host side data toggle. for + consistency also clear the data toggle on the device. */ + usbi_dbg (ctx, "calling ClearPipeStallBothEnds to clear the data toggle bit"); + kresult = (*IOINTERFACE(cInterface))->ClearPipeStallBothEnds(IOINTERFACE(cInterface), pipeRef); + } return darwin_to_libusb (kresult); } @@ -2313,7 +2574,7 @@ static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) (void) ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface); - (*(cInterface->interface))->WritePipe (cInterface->interface, pipeRef, transfer->buffer, 0); + (*IOINTERFACE(cInterface))->WritePipe (IOINTERFACE(cInterface), pipeRef, transfer->buffer, 0); } tpriv->result = result; @@ -2389,29 +2650,38 @@ static int darwin_handle_transfer_completion (struct usbi_transfer *itransfer) { return usbi_handle_transfer_completion (itransfer, darwin_transfer_status (itransfer, tpriv->result)); } -#if !defined(HAVE_CLOCK_GETTIME) void usbi_get_monotonic_time(struct timespec *tp) { - mach_timespec_t sys_time; +/* Check if the SDK is new enough to declare clock_gettime(), and the deployment target is at least 10.12. */ +#if ((MAC_OS_X_VERSION_MAX_ALLOWED >= 101200) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)) + clock_gettime(CLOCK_MONOTONIC, tp); +#else + mach_timebase_info_data_t machTimeBaseInfo; + mach_timebase_info(&machTimeBaseInfo); - /* use system boot time as reference for the monotonic clock */ - clock_get_time (clock_monotonic, &sys_time); + uint64_t uptime = mach_absolute_time(); + uint64_t uptimeNano = uptime * machTimeBaseInfo.numer / machTimeBaseInfo.denom; - tp->tv_sec = sys_time.tv_sec; - tp->tv_nsec = sys_time.tv_nsec; + uint64_t uptimeSeconds = uptimeNano / NSEC_PER_SEC; + uint64_t uptimeNanoRemainder = uptimeNano - (uptimeSeconds * NSEC_PER_SEC); + + tp->tv_sec = uptimeSeconds; + tp->tv_nsec = uptimeNanoRemainder; +#endif } void usbi_get_real_time(struct timespec *tp) { - mach_timespec_t sys_time; - - /* CLOCK_REALTIME represents time since the epoch */ - clock_get_time (clock_realtime, &sys_time); - - tp->tv_sec = sys_time.tv_sec; - tp->tv_nsec = sys_time.tv_nsec; -} +/* Check if the SDK is new enough to declare clock_gettime(), and the deployment target is at least 10.12. */ +#if ((MAC_OS_X_VERSION_MAX_ALLOWED >= 101200) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)) + clock_gettime(CLOCK_REALTIME, tp); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + tp->tv_sec = tv.tv_sec; + tp->tv_nsec = tv.tv_usec * NSEC_PER_USEC; #endif +} -#if InterfaceVersion >= 550 +#if MAX_INTERFACE_VERSION >= 550 static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32_t num_streams, unsigned char *endpoints, int num_endpoints) { struct darwin_interface *cInterface; @@ -2425,7 +2695,7 @@ static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32 return rc; } - (*(cInterface->interface))->SupportsStreams (cInterface->interface, pipeRef, &supportsStreams); + (*IOINTERFACE_V(cInterface, 550))->SupportsStreams (IOINTERFACE(cInterface), pipeRef, &supportsStreams); if (num_streams > supportsStreams) num_streams = supportsStreams; } @@ -2438,7 +2708,7 @@ static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32 for (i = 0 ; i < num_endpoints ; ++i) { (void) ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface); - rc = (*(cInterface->interface))->CreateStreams (cInterface->interface, pipeRef, num_streams); + rc = (*IOINTERFACE_V(cInterface, 550))->CreateStreams (IOINTERFACE(cInterface), pipeRef, num_streams); if (kIOReturnSuccess != rc) return darwin_to_libusb(rc); } @@ -2457,11 +2727,11 @@ static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigne if (0 != (rc = ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface))) return rc; - (*(cInterface->interface))->SupportsStreams (cInterface->interface, pipeRef, &supportsStreams); + (*IOINTERFACE_V(cInterface, 550))->SupportsStreams (IOINTERFACE(cInterface), pipeRef, &supportsStreams); if (0 == supportsStreams) return LIBUSB_ERROR_INVALID_PARAM; - rc = (*(cInterface->interface))->CreateStreams (cInterface->interface, pipeRef, 0); + rc = (*IOINTERFACE_V(cInterface, 550))->CreateStreams (IOINTERFACE(cInterface), pipeRef, 0); if (kIOReturnSuccess != rc) return darwin_to_libusb(rc); } @@ -2470,7 +2740,7 @@ static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigne } #endif -#if InterfaceVersion >= 700 +#if MAX_INTERFACE_VERSION >= 700 /* macOS APIs for getting entitlement values */ @@ -2504,15 +2774,10 @@ static int darwin_reload_device (struct libusb_device_handle *dev_handle) { struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); enum libusb_error err; - usbi_mutex_lock(&darwin_cached_devices_lock); - (*(dpriv->device))->Release(dpriv->device); - dpriv->device = darwin_device_from_service (HANDLE_CTX (dev_handle), dpriv->service); - if (!dpriv->device) { - err = LIBUSB_ERROR_NO_DEVICE; - } else { - err = LIBUSB_SUCCESS; - } - usbi_mutex_unlock(&darwin_cached_devices_lock); + usbi_mutex_lock(&darwin_cached_devices_mutex); + (*dpriv->device)->Release(dpriv->device); + err = darwin_device_from_service (HANDLE_CTX (dev_handle), dpriv->service, &dpriv->device); + usbi_mutex_unlock(&darwin_cached_devices_mutex); return err; } @@ -2526,8 +2791,7 @@ static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, enum libusb_error err; struct libusb_context *ctx = HANDLE_CTX (dev_handle); - if (HAS_CAPTURE_DEVICE()) { - } else { + if (get_interface_interface_version() < 700) { return LIBUSB_ERROR_NOT_SUPPORTED; } @@ -2570,8 +2834,7 @@ static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, UNUSED(interface); struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); - if (HAS_CAPTURE_DEVICE()) { - } else { + if (get_interface_interface_version() < 700) { return LIBUSB_ERROR_NOT_SUPPORTED; } @@ -2638,14 +2901,14 @@ const struct usbi_os_backend usbi_backend = { .clear_halt = darwin_clear_halt, .reset_device = darwin_reset_device, -#if InterfaceVersion >= 550 +#if MAX_INTERFACE_VERSION >= 550 .alloc_streams = darwin_alloc_streams, .free_streams = darwin_free_streams, #endif .kernel_driver_active = darwin_kernel_driver_active, -#if InterfaceVersion >= 700 +#if MAX_INTERFACE_VERSION >= 700 .detach_kernel_driver = darwin_detach_kernel_driver, .attach_kernel_driver = darwin_attach_kernel_driver, .claim_interface = darwin_capture_claim_interface, diff --git a/mac/libusb/os/darwin_usb.h b/mac/libusb/os/darwin_usb.h index 7b72fffb8..86646bfee 100644 --- a/mac/libusb/os/darwin_usb.h +++ b/mac/libusb/os/darwin_usb.h @@ -1,7 +1,7 @@ /* * darwin backend for libusb 1.0 - * Copyright © 2008-2019 Nathan Hjelm - * Copyright © 2019 Google LLC. All rights reserved. + * Copyright © 2008-2023 Nathan Hjelm + * Copyright © 2019-2023 Google LLC. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -36,117 +36,48 @@ /* IOUSBInterfaceInferface */ -/* New in OS 10.12.0. */ -#if defined (kIOUSBInterfaceInterfaceID800) - -#define usb_interface_t IOUSBInterfaceInterface800 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID800 -#define InterfaceVersion 800 - -/* New in OS 10.10.0. */ -#elif defined (kIOUSBInterfaceInterfaceID700) - -#define usb_interface_t IOUSBInterfaceInterface700 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID700 -#define InterfaceVersion 700 - -/* New in OS 10.9.0. */ -#elif defined (kIOUSBInterfaceInterfaceID650) - -#define usb_interface_t IOUSBInterfaceInterface650 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID650 -#define InterfaceVersion 650 - -/* New in OS 10.8.2 but can't test deployment target to that granularity, so round up. */ -#elif defined (kIOUSBInterfaceInterfaceID550) - -#define usb_interface_t IOUSBInterfaceInterface550 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID550 -#define InterfaceVersion 550 - -/* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */ -#elif defined (kIOUSBInterfaceInterfaceID500) - -#define usb_interface_t IOUSBInterfaceInterface500 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID500 -#define InterfaceVersion 500 - -/* New in OS 10.5.0. */ -#elif defined (kIOUSBInterfaceInterfaceID300) - -#define usb_interface_t IOUSBInterfaceInterface300 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300 -#define InterfaceVersion 300 - -/* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */ -#elif defined (kIOUSBInterfaceInterfaceID245) - -#define usb_interface_t IOUSBInterfaceInterface245 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245 -#define InterfaceVersion 245 - -/* New in OS 10.4.0. */ -#elif defined (kIOUSBInterfaceInterfaceID220) - -#define usb_interface_t IOUSBInterfaceInterface220 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220 -#define InterfaceVersion 220 - +#if defined(kIOUSBInterfaceInterfaceID800) +#define MAX_INTERFACE_VERSION 800 +#elif defined(kIOUSBInterfaceInterfaceID700) +#define MAX_INTERFACE_VERSION 700 +#elif defined(kIOUSBInterfaceInterfaceID650) +#define MAX_INTERFACE_VERSION 650 +#elif defined(kIOUSBInterfaceInterfaceID550) +#define MAX_INTERFACE_VERSION 550 +#elif defined(kIOUSBInterfaceInterfaceID245) +#define MAX_INTERFACE_VERSION 245 #else - -#error "IOUSBFamily is too old. Please upgrade your SDK and/or deployment target" - +#define MAX_INTERFACE_VERSION 220 #endif -/* IOUSBDeviceInterface */ - -/* New in OS 10.9.0. */ -#if defined (kIOUSBDeviceInterfaceID650) - -#define usb_device_t IOUSBDeviceInterface650 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID650 -#define DeviceVersion 650 - -/* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */ -#elif defined (kIOUSBDeviceInterfaceID500) - -#define usb_device_t IOUSBDeviceInterface500 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID500 -#define DeviceVersion 500 - -/* New in OS 10.5.4 but can't test deployment target to that granularity, so round up. */ -#elif defined (kIOUSBDeviceInterfaceID320) - -#define usb_device_t IOUSBDeviceInterface320 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID320 -#define DeviceVersion 320 - -/* New in OS 10.5.0. */ -#elif defined (kIOUSBDeviceInterfaceID300) - -#define usb_device_t IOUSBDeviceInterface300 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID300 -#define DeviceVersion 300 +/* set to the minimum version and casted up as needed. */ +typedef IOUSBInterfaceInterface220 **usb_interface_t; -/* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */ -#elif defined (kIOUSBDeviceInterfaceID245) +#define IOINTERFACE0(darwin_interface, version) ((IOUSBInterfaceInterface ## version **) (darwin_interface)->interface) +#define IOINTERFACE_V(darwin_interface, version) IOINTERFACE0(darwin_interface, version) +#define IOINTERFACE(darwin_interface) ((darwin_interface)->interface) -#define usb_device_t IOUSBDeviceInterface245 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID245 -#define DeviceVersion 245 - -/* New in OS 10.2.3 but can't test deployment target to that granularity, so round up. */ -#elif defined (kIOUSBDeviceInterfaceID197) - -#define usb_device_t IOUSBDeviceInterface197 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID197 -#define DeviceVersion 197 +/* IOUSBDeviceInterface */ +#if defined(kIOUSBDeviceInterfaceID650) +#define MAX_DEVICE_VERSION 650 +#elif defined(kIOUSBDeviceInterfaceID500) +#define MAX_DEVICE_VERSION 500 +#elif defined(kIOUSBDeviceInterfaceID320) +#define MAX_DEVICE_VERSION 320 +#elif defined(kIOUSBDeviceInterfaceID300) +#define MAX_DEVICE_VERSION 300 +#elif defined(kIOUSBDeviceInterfaceID245) +#define MAX_DEVICE_VERSION 245 #else +#define MAX_DEVICE_VERSION 197 +#endif -#error "IOUSBFamily is too old. Please upgrade your SDK and/or deployment target" +/* set to the minimum version and casted up as needed */ +typedef IOUSBDeviceInterface197 **usb_device_t; -#endif +#define IODEVICE0(darwin_device, version) ((IOUSBDeviceInterface ## version **)(darwin_device)) +#define IODEVICE_V(darwin_device, version) IODEVICE0(darwin_device, version) #if !defined(kIOUSBHostInterfaceClassName) #define kIOUSBHostInterfaceClassName "IOUSBHostInterface" @@ -160,15 +91,13 @@ #define IO_OBJECT_NULL ((io_object_t) 0) #endif -/* Testing availability */ -#ifndef __has_builtin - #define __has_builtin(x) 0 // Compatibility with non-clang compilers. -#endif -#if __has_builtin(__builtin_available) - #define HAS_CAPTURE_DEVICE() __builtin_available(macOS 10.10, *) -#else - #define HAS_CAPTURE_DEVICE() 0 -#endif +/* returns the current macOS version in a format similar to the + * MAC_OS_X_VERSION_MIN_REQUIRED macro. + * Examples: + * 10.1.5 -> 100105 + * 13.3.0 -> 130300 + */ +uint32_t get_running_version(void); typedef IOCFPlugInInterface *io_cf_plugin_ref_t; typedef IONotificationPortRef io_notification_port_t; @@ -182,7 +111,7 @@ struct darwin_cached_device { UInt64 session; USBDeviceAddress address; char sys_path[21]; - usb_device_t **device; + usb_device_t device; io_service_t service; int open_count; UInt8 first_config, active_config, port; @@ -201,7 +130,7 @@ struct darwin_device_handle_priv { CFRunLoopSourceRef cfSource; struct darwin_interface { - usb_interface_t **interface; + usb_interface_t interface; uint8_t num_endpoints; CFRunLoopSourceRef cfSource; uint64_t frames[256]; diff --git a/mac/libusb/os/events_posix.c b/mac/libusb/os/events_posix.c index 715a2d551..4056dae2e 100644 --- a/mac/libusb/os/events_posix.c +++ b/mac/libusb/os/events_posix.c @@ -28,6 +28,31 @@ #ifdef HAVE_TIMERFD #include #endif + +#ifdef __EMSCRIPTEN__ +/* On Emscripten `pipe` does not conform to the spec and does not block + * until events are available, which makes it unusable for event system + * and often results in deadlocks when `pipe` is in a loop like it is + * in libusb. + * + * Therefore use a custom event system based on browser event emitters. */ +#include +#include +#include + +EM_ASYNC_JS(void, em_libusb_wait_async, (const _Atomic int* ptr, int expected_value, int timeout), { + await Atomics.waitAsync(HEAP32, ptr >> 2, expected_value, timeout).value; +}); + +static void em_libusb_wait(const _Atomic int *ptr, int expected_value, int timeout) +{ + if (emscripten_is_main_runtime_thread()) { + em_libusb_wait_async(ptr, expected_value, timeout); + } else { + emscripten_atomic_wait_u32((int*)ptr, expected_value, 1000000LL * timeout); + } +} +#endif #include #ifdef HAVE_EVENTFD @@ -131,6 +156,10 @@ void usbi_signal_event(usbi_event_t *event) r = write(EVENT_WRITE_FD(event), &dummy, sizeof(dummy)); if (r != sizeof(dummy)) usbi_warn(NULL, "event write failed"); +#ifdef __EMSCRIPTEN__ + event->has_event = 1; + emscripten_atomic_notify(&event->has_event, EMSCRIPTEN_NOTIFY_ALL_WAITERS); +#endif } void usbi_clear_event(usbi_event_t *event) @@ -141,6 +170,9 @@ void usbi_clear_event(usbi_event_t *event) r = read(EVENT_READ_FD(event), &dummy, sizeof(dummy)); if (r != sizeof(dummy)) usbi_warn(NULL, "event read failed"); +#ifdef __EMSCRIPTEN__ + event->has_event = 0; +#endif } #ifdef HAVE_TIMERFD @@ -223,6 +255,14 @@ int usbi_wait_for_events(struct libusb_context *ctx, int internal_fds, num_ready; usbi_dbg(ctx, "poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms); +#ifdef __EMSCRIPTEN__ + // Emscripten's poll doesn't actually block, so we need to use an out-of-band + // waiting signal. + em_libusb_wait(&ctx->event.has_event, 0, timeout_ms); + // Emscripten ignores timeout_ms, but set it to 0 for future-proofing in case + // they ever implement real poll. + timeout_ms = 0; +#endif num_ready = poll(fds, nfds, timeout_ms); usbi_dbg(ctx, "poll() returned %d", num_ready); if (num_ready == 0) { diff --git a/mac/libusb/os/events_posix.h b/mac/libusb/os/events_posix.h index d81b5c4df..4bd7f0fa2 100644 --- a/mac/libusb/os/events_posix.h +++ b/mac/libusb/os/events_posix.h @@ -36,6 +36,9 @@ typedef struct usbi_event { #else typedef struct usbi_event { int pipefd[2]; +#ifdef __EMSCRIPTEN__ + _Atomic int has_event; +#endif } usbi_event_t; #define USBI_EVENT_OS_HANDLE(e) ((e)->pipefd[0]) #define USBI_EVENT_POLL_EVENTS POLLIN diff --git a/mac/libusb/os/threads_posix.c b/mac/libusb/os/threads_posix.c index 0e0e22134..0079fd598 100644 --- a/mac/libusb/os/threads_posix.c +++ b/mac/libusb/os/threads_posix.c @@ -32,8 +32,6 @@ #elif defined(__NetBSD__) # include #elif defined(__OpenBSD__) -# define _BSD_SOURCE -# include # include #elif defined(__sun__) # include @@ -109,9 +107,7 @@ unsigned int usbi_get_tid(void) #elif defined(__NetBSD__) tid = _lwp_self(); #elif defined(__OpenBSD__) - /* The following only works with OpenBSD > 5.1 as it requires - * real thread support. For 5.1 and earlier, -1 is returned. */ - tid = syscall(SYS_getthrid); + tid = getthrid(); #elif defined(__sun__) tid = _lwp_self(); #else diff --git a/mac/libusb/strerror.c b/mac/libusb/strerror.c index 9445fa9e8..eb355034b 100644 --- a/mac/libusb/strerror.c +++ b/mac/libusb/strerror.c @@ -28,7 +28,7 @@ *
  • Download the latest \c strerror.c from:
    * https://raw.github.com/libusb/libusb/master/libusb/strerror.c
  • *
  • Open the file in an UTF-8 capable editor
  • - *
  • Add the 2 letter ISO 639-1 + *
  • Add the 2 letter ISO 639-1 * code for your locale at the end of \c usbi_locale_supported[]
    * Eg. for Chinese, you would add "zh" so that: * \code... usbi_locale_supported[] = { "en", "nl", "fr" };\endcode @@ -160,7 +160,7 @@ static const char * const (*usbi_error_strings)[LIBUSB_ERROR_COUNT] = &usbi_loca * If libusb_setlocale() is not called, all messages will be in English. * * The following functions return translatable strings: libusb_strerror(). - * Note that the libusb log messages controlled through libusb_set_debug() + * Note that the libusb log messages controlled through LIBUSB_OPTION_LOG_LEVEL * are not translated, they are always in English. * * For POSIX UTF-8 environments if you want libusb to follow the standard @@ -169,9 +169,9 @@ static const char * const (*usbi_error_strings)[LIBUSB_ERROR_COUNT] = &usbi_loca * * \param locale locale-string in the form of lang[_country_region][.codeset] * or lang[-region], where lang is a 2 letter ISO 639-1 code - * \returns LIBUSB_SUCCESS on success - * \returns LIBUSB_ERROR_INVALID_PARAM if the locale doesn't meet the requirements - * \returns LIBUSB_ERROR_NOT_FOUND if the requested language is not supported + * \returns \ref LIBUSB_SUCCESS on success + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the locale doesn't meet the requirements + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the requested language is not supported * \returns a LIBUSB_ERROR code on other errors */ diff --git a/mac/libusb/sync.c b/mac/libusb/sync.c index 1fa1f0be5..146cce23c 100644 --- a/mac/libusb/sync.c +++ b/mac/libusb/sync.c @@ -34,10 +34,15 @@ static void LIBUSB_CALL sync_transfer_cb(struct libusb_transfer *transfer) { + usbi_dbg(TRANSFER_CTX(transfer), "actual_length=%d", transfer->actual_length); + int *completed = transfer->user_data; *completed = 1; - usbi_dbg(TRANSFER_CTX(transfer), "actual_length=%d", transfer->actual_length); - /* caller interprets result and frees transfer */ + /* + * Right after setting 'completed', another thread might free the transfer, so don't + * access it beyond this point. The instantiating thread (not necessarily the + * current one) interprets the result and frees the transfer. + */ } static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer) @@ -85,12 +90,12 @@ static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer) * before giving up due to no response being received. For an unlimited * timeout, use value 0. * \returns on success, the number of bytes actually transferred - * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out - * \returns LIBUSB_ERROR_PIPE if the control request was not supported by the + * \returns \ref LIBUSB_ERROR_TIMEOUT if the transfer timed out + * \returns \ref LIBUSB_ERROR_PIPE if the control request was not supported by the * device - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - * \returns LIBUSB_ERROR_BUSY if called from event handling context - * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_BUSY if called from event handling context + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than * the operating system and/or hardware can support (see \ref asynclimits) * \returns another LIBUSB_ERROR code on other failures */ @@ -260,14 +265,14 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, * timeout, use value 0. * * \returns 0 on success (and populates transferred) - * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates + * \returns \ref LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates * transferred) - * \returns LIBUSB_ERROR_PIPE if the endpoint halted - * \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see + * \returns \ref LIBUSB_ERROR_PIPE if the endpoint halted + * \returns \ref LIBUSB_ERROR_OVERFLOW if the device offered more data, see * \ref libusb_packetoverflow - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - * \returns LIBUSB_ERROR_BUSY if called from event handling context - * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_BUSY if called from event handling context + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than * the operating system and/or hardware can support (see \ref asynclimits) * \returns another LIBUSB_ERROR code on other failures */ @@ -315,13 +320,13 @@ int API_EXPORTED libusb_bulk_transfer(libusb_device_handle *dev_handle, * timeout, use value 0. * * \returns 0 on success (and populates transferred) - * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out - * \returns LIBUSB_ERROR_PIPE if the endpoint halted - * \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see + * \returns \ref LIBUSB_ERROR_TIMEOUT if the transfer timed out + * \returns \ref LIBUSB_ERROR_PIPE if the endpoint halted + * \returns \ref LIBUSB_ERROR_OVERFLOW if the device offered more data, see * \ref libusb_packetoverflow - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - * \returns LIBUSB_ERROR_BUSY if called from event handling context - * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_BUSY if called from event handling context + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than * the operating system and/or hardware can support (see \ref asynclimits) * \returns another LIBUSB_ERROR code on other error */ diff --git a/mac/libusb/version.h b/mac/libusb/version.h index fe95d84b6..ca9df20ff 100644 --- a/mac/libusb/version.h +++ b/mac/libusb/version.h @@ -7,7 +7,7 @@ #define LIBUSB_MINOR 0 #endif #ifndef LIBUSB_MICRO -#define LIBUSB_MICRO 26 +#define LIBUSB_MICRO 27 #endif #ifndef LIBUSB_NANO #define LIBUSB_NANO 0 diff --git a/mac/libusb/version_nano.h b/mac/libusb/version_nano.h index dbd5d5f5b..a6165f334 100644 --- a/mac/libusb/version_nano.h +++ b/mac/libusb/version_nano.h @@ -1 +1 @@ -#define LIBUSB_NANO 11724 +#define LIBUSB_NANO 11882 -- 2.30.2